diff --git a/README.md b/README.md
index 6fd1af4e20..2b93a47b01 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ compatible with any modern printer based on the RepRap toolchain, including all
those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works
with Mach3, LinuxCNC and Machinekit controllers.
-PrusaSlicer is based on [Slic3r](https://github.com/Slic3r/Slic3r) by Alessandro Ranelucci and the RepRap community.
+PrusaSlicer is based on [Slic3r](https://github.com/Slic3r/Slic3r) by Alessandro Ranellucci and the RepRap community.
See the [project homepage](https://www.prusa3d.com/slic3r-prusa-edition/) and
the [documentation directory](doc/) for more information.
diff --git a/resources/icons/add.svg b/resources/icons/add.svg
index 8a9b253de7..37050d7481 100644
--- a/resources/icons/add.svg
+++ b/resources/icons/add.svg
@@ -1,17 +1,22 @@
-
+
diff --git a/resources/icons/arrange.svg b/resources/icons/arrange.svg
index 4f30e979e3..62cf939e9f 100644
--- a/resources/icons/arrange.svg
+++ b/resources/icons/arrange.svg
@@ -1,24 +1,23 @@
-
+
diff --git a/resources/icons/copy.svg b/resources/icons/copy.svg
index 9b8430dd79..345c2590be 100644
--- a/resources/icons/copy.svg
+++ b/resources/icons/copy.svg
@@ -1,37 +1,29 @@
-
+
diff --git a/resources/icons/delete_all.svg b/resources/icons/delete_all.svg
index 80e2e503cb..dfa9438129 100644
--- a/resources/icons/delete_all.svg
+++ b/resources/icons/delete_all.svg
@@ -1,31 +1,17 @@
-
+
diff --git a/resources/icons/instance_add.svg b/resources/icons/instance_add.svg
index 5ef492cfae..a466c51dbf 100644
--- a/resources/icons/instance_add.svg
+++ b/resources/icons/instance_add.svg
@@ -1,50 +1,46 @@
-
+
diff --git a/resources/icons/instance_remove.svg b/resources/icons/instance_remove.svg
index 466752ea89..7f9b4f7e1c 100644
--- a/resources/icons/instance_remove.svg
+++ b/resources/icons/instance_remove.svg
@@ -1,49 +1,42 @@
-
+
diff --git a/resources/icons/paste.svg b/resources/icons/paste.svg
index 028ffb8ea0..bcfe567de3 100644
--- a/resources/icons/paste.svg
+++ b/resources/icons/paste.svg
@@ -1,27 +1,22 @@
-
+
diff --git a/resources/icons/redo_toolbar.svg b/resources/icons/redo_toolbar.svg
index d005f83736..d2aca2cc7d 100644
--- a/resources/icons/redo_toolbar.svg
+++ b/resources/icons/redo_toolbar.svg
@@ -1,13 +1,17 @@
-
+
diff --git a/resources/icons/remove.svg b/resources/icons/remove.svg
index acd21256cd..1bb830d91c 100644
--- a/resources/icons/remove.svg
+++ b/resources/icons/remove.svg
@@ -1,44 +1,60 @@
-
+
diff --git a/resources/icons/seam.svg b/resources/icons/seam.svg
index 119fb6afcc..a7e7980cc0 100644
--- a/resources/icons/seam.svg
+++ b/resources/icons/seam.svg
@@ -1,42 +1,35 @@
-
+
diff --git a/resources/icons/search_.svg b/resources/icons/search_.svg
index 679bb30f71..2985ceb561 100644
--- a/resources/icons/search_.svg
+++ b/resources/icons/search_.svg
@@ -1,4 +1,26 @@
-
\ No newline at end of file
+
+
+
diff --git a/resources/icons/search_blink.svg b/resources/icons/search_blink.svg
new file mode 100644
index 0000000000..d005f83736
--- /dev/null
+++ b/resources/icons/search_blink.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/resources/icons/settings.svg b/resources/icons/settings.svg
new file mode 100644
index 0000000000..db5bf458d7
--- /dev/null
+++ b/resources/icons/settings.svg
@@ -0,0 +1,68 @@
+
+
+
diff --git a/resources/icons/split_objects.svg b/resources/icons/split_objects.svg
index a7ccc5df86..e822fd35aa 100644
--- a/resources/icons/split_objects.svg
+++ b/resources/icons/split_objects.svg
@@ -1,19 +1,20 @@
-
+
diff --git a/resources/icons/split_parts.svg b/resources/icons/split_parts.svg
index 82a2927706..5cfef0f330 100644
--- a/resources/icons/split_parts.svg
+++ b/resources/icons/split_parts.svg
@@ -1,18 +1,20 @@
-
+
diff --git a/resources/icons/undo_toolbar.svg b/resources/icons/undo_toolbar.svg
index 15778a7baf..2fc25bf737 100644
--- a/resources/icons/undo_toolbar.svg
+++ b/resources/icons/undo_toolbar.svg
@@ -1,13 +1,17 @@
-
+
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 05e84b9416..a12ad8bb73 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -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
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 9224b04594..e0f2865f0d 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -255,18 +255,24 @@ extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
}
template
-inline void its_transform(indexed_triangle_set &its, const Eigen::Transform& t)
+inline void its_transform(indexed_triangle_set &its, const Eigen::Transform& t, bool fix_left_handed = false)
{
//const Eigen::Matrix r = t.matrix().template block<3, 3>(0, 0);
for (stl_vertex &v : its.vertices)
v = (t * v.template cast()).template cast().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
-inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix& m)
+inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix& m, bool fix_left_handed = false)
{
- for (stl_vertex &v : its.vertices)
+ for (stl_vertex &v : its.vertices)
v = (m * v.template cast()).template cast().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);
diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp
index 17d918aeb3..964133faae 100644
--- a/src/libslic3r/AABBTreeIndirect.hpp
+++ b/src/libslic3r/AABBTreeIndirect.hpp
@@ -283,7 +283,7 @@ namespace detail {
template
std::enable_if_t::value && std::is_same::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(origin.data()), const_cast(dir.data()),
const_cast(v0.data()), const_cast(v1.data()), const_cast(v2.data()),
&t, &u, &v);
@@ -291,7 +291,7 @@ namespace detail {
template
std::enable_if_t::value && !std::is_same::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;
Vector w0 = v0.template cast();
Vector w1 = v1.template cast();
@@ -302,7 +302,7 @@ namespace detail {
template
std::enable_if_t::value && std::is_same::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;
Vector o = origin.template cast();
Vector d = dir.template cast();
@@ -311,7 +311,7 @@ namespace detail {
template
std::enable_if_t::value && ! std::is_same::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;
Vector o = origin.template cast();
Vector d = dir.template cast();
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 5892b4a30d..72c4fd0e92 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -1,6 +1,7 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp"
#include "AppConfig.hpp"
+#include "Exception.hpp"
#include
#include
@@ -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());
diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp
index e3f9395092..eb4e042a08 100644
--- a/src/libslic3r/BoundingBox.cpp
+++ b/src/libslic3r/BoundingBox.cpp
@@ -75,6 +75,7 @@ BoundingBoxBase::merge(const PointClass &point)
}
}
template void BoundingBoxBase::merge(const Point &point);
+template void BoundingBoxBase::merge(const Vec2f &point);
template void BoundingBoxBase::merge(const Vec2d &point);
template void
@@ -101,6 +102,7 @@ BoundingBoxBase::merge(const BoundingBoxBase &bb)
}
}
template void BoundingBoxBase::merge(const BoundingBoxBase &bb);
+template void BoundingBoxBase::merge(const BoundingBoxBase &bb);
template void BoundingBoxBase::merge(const BoundingBoxBase &bb);
template void
@@ -115,6 +117,7 @@ BoundingBox3Base::merge(const PointClass &point)
this->defined = true;
}
}
+template void BoundingBox3Base::merge(const Vec3f &point);
template void BoundingBox3Base::merge(const Vec3d &point);
template void
@@ -147,6 +150,7 @@ BoundingBoxBase::size() const
return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1));
}
template Point BoundingBoxBase::size() const;
+template Vec2f BoundingBoxBase::size() const;
template Vec2d BoundingBoxBase::size() const;
template PointClass
@@ -154,6 +158,7 @@ BoundingBox3Base::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::size() const;
template Vec3d BoundingBox3Base::size() const;
template double BoundingBoxBase::radius() const
@@ -200,6 +205,7 @@ BoundingBoxBase::center() const
return (this->min + this->max) / 2;
}
template Point BoundingBoxBase::center() const;
+template Vec2f BoundingBoxBase::center() const;
template Vec2d BoundingBoxBase::center() const;
template PointClass
@@ -207,6 +213,7 @@ BoundingBox3Base::center() const
{
return (this->min + this->max) / 2;
}
+template Vec3f BoundingBox3Base::center() const;
template Vec3d BoundingBox3Base::center() const;
template coordf_t
@@ -215,6 +222,7 @@ BoundingBox3Base::max_size() const
PointClass s = size();
return std::max(s(0), std::max(s(1), s(2)));
}
+template coordf_t BoundingBox3Base::max_size() const;
template coordf_t BoundingBox3Base::max_size() const;
// Align a coordinate to a grid. The coordinate may be negative,
diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp
index 08f01d8d85..065476cb28 100644
--- a/src/libslic3r/BoundingBox.hpp
+++ b/src/libslic3r/BoundingBox.hpp
@@ -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& 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::const_iterator it = points.begin();
this->min = *it;
@@ -65,10 +68,12 @@ public:
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
BoundingBoxBase(pmin, pmax)
{ if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; }
+ BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
+ BoundingBoxBase(p1, p1) { merge(p2); merge(p3); }
BoundingBox3Base(const std::vector& 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::const_iterator it = points.begin();
this->min = *it;
this->max = *it;
@@ -109,24 +114,32 @@ extern template void BoundingBoxBase::scale(double factor);
extern template void BoundingBoxBase::offset(coordf_t delta);
extern template void BoundingBoxBase::offset(coordf_t delta);
extern template void BoundingBoxBase::merge(const Point &point);
+extern template void BoundingBoxBase::merge(const Vec2f &point);
extern template void BoundingBoxBase::merge(const Vec2d &point);
extern template void BoundingBoxBase::merge(const Points &points);
extern template void BoundingBoxBase::merge(const Pointfs &points);
extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb);
+extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb);
extern template void BoundingBoxBase::merge(const BoundingBoxBase &bb);
extern template Point BoundingBoxBase::size() const;
+extern template Vec2f BoundingBoxBase::size() const;
extern template Vec2d BoundingBoxBase::size() const;
extern template double BoundingBoxBase::radius() const;
extern template double BoundingBoxBase::radius() const;
extern template Point BoundingBoxBase::center() const;
+extern template Vec2f BoundingBoxBase::center() const;
extern template Vec2d BoundingBoxBase::center() const;
+extern template void BoundingBox3Base::merge(const Vec3f &point);
extern template void BoundingBox3Base::merge(const Vec3d &point);
extern template void BoundingBox3Base::merge(const Pointf3s &points);
extern template void BoundingBox3Base::merge(const BoundingBox3Base &bb);
+extern template Vec3f BoundingBox3Base::size() const;
extern template Vec3d BoundingBox3Base::size() const;
extern template double BoundingBox3Base::radius() const;
extern template void BoundingBox3Base::offset(coordf_t delta);
+extern template Vec3f BoundingBox3Base::center() const;
extern template Vec3d BoundingBox3Base::center() const;
+extern template coordf_t BoundingBox3Base::max_size() const;
extern template coordf_t BoundingBox3Base::max_size() const;
class BoundingBox : public BoundingBoxBase
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index e30811133a..ee41248f3b 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -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
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index f3f365b478..25ef93430f 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -5,7 +5,6 @@
#include
#include
#include
-#include // std::runtime_error
#include
#include
#include
@@ -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(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(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();
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 87e0208986..28b28b405a 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -13,6 +13,7 @@
#include
#include "libslic3r.h"
#include "clonable_ptr.hpp"
+#include "Exception.hpp"
#include "Point.hpp"
#include
@@ -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 &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*>(rhs));
this->value = static_cast*>(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*>(&rhs));
return this->value == static_cast*>(&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*>(rhs));
this->values = static_cast*>(rhs)->values;
}
@@ -256,12 +257,12 @@ public:
if (opt->type() == this->type()) {
auto other = static_cast*>(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*>(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*>(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*>(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*>(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*>(&rhs));
return this->values == static_cast*>(&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*>(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*>(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*>(&rhs));
return vectors_equal(this->values, static_cast*>(&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 &v1, const std::vector &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(&rhs));
return *this == *static_cast(&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(rhs));
*this = *static_cast(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: Comparing incompatible types");
+ throw Slic3r::RuntimeError("ConfigOptionEnum: Comparing incompatible types");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum
return this->value == (T)rhs.getInt();
}
void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type())
- throw std::runtime_error("ConfigOptionEnum: Assigning an incompatible type");
+ throw Slic3r::RuntimeError("ConfigOptionEnum: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum
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
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
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(opt)); break;
case coPercents: archive(*static_cast(opt));break;
case coBools: archive(*static_cast(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(opt)); break;
case coBools: archive(*static_cast(opt)); break;
case coEnum: archive(*static_cast(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.
diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp
index daaab47555..5bdd5055ec 100644
--- a/src/libslic3r/ExPolygon.cpp
+++ b/src/libslic3r/ExPolygon.cpp
@@ -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 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::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
}
diff --git a/src/libslic3r/Exception.hpp b/src/libslic3r/Exception.hpp
new file mode 100644
index 0000000000..8ec9f20c81
--- /dev/null
+++ b/src/libslic3r/Exception.hpp
@@ -0,0 +1,28 @@
+#ifndef _libslic3r_Exception_h_
+#define _libslic3r_Exception_h_
+
+#include
+
+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_
diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp
index dfece6949b..5e40ab32ec 100644
--- a/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -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.;
}
};
diff --git a/src/libslic3r/FileParserError.hpp b/src/libslic3r/FileParserError.hpp
index 3f560fa4f5..b7e63d84e0 100644
--- a/src/libslic3r/FileParserError.hpp
+++ b/src/libslic3r/FileParserError.hpp
@@ -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;
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index d68bc7afb3..03aa798dc4 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -318,7 +318,7 @@ void export_group_fills_to_svg(const char *path, const std::vector
#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;
diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp
index 3b9212230d..eebded55bf 100644
--- a/src/libslic3r/Fill/FillAdaptive.cpp
+++ b/src/libslic3r/Fill/FillAdaptive.cpp
@@ -2,14 +2,268 @@
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../Geometry.hpp"
-#include "../AABBTreeIndirect.hpp"
#include "../Layer.hpp"
#include "../Print.hpp"
#include "../ShortestPath.hpp"
#include "FillAdaptive.hpp"
+// for indexed_triangle_set
+#include
+
+#include
+#include
+
+// Boost pool: Don't use mutexes to synchronize memory allocation.
+#define BOOST_POOL_NO_MT
+#include
+
namespace Slic3r {
+namespace FillAdaptive {
+
+// Derived from https://github.com/juj/MathGeoLib/blob/master/src/Geometry/Triangle.cpp
+// The AABB-Triangle test implementation is based on the pseudo-code in
+// Christer Ericson's Real-Time Collision Detection, pp. 169-172. It is
+// practically a standard SAT test.
+//
+// Original MathGeoLib benchmark:
+// Best: 17.282 nsecs / 46.496 ticks, Avg: 17.804 nsecs, Worst: 18.434 nsecs
+//
+//FIXME Vojtech: The MathGeoLib contains a vectorized implementation.
+template
+bool triangle_AABB_intersects(const Vector &a, const Vector &b, const Vector &c, const BoundingBoxBase &aabb)
+{
+ using Scalar = typename Vector::Scalar;
+
+ Vector tMin = a.cwiseMin(b.cwiseMin(c));
+ Vector tMax = a.cwiseMax(b.cwiseMax(c));
+
+ if (tMin.x() >= aabb.max.x() || tMax.x() <= aabb.min.x()
+ || tMin.y() >= aabb.max.y() || tMax.y() <= aabb.min.y()
+ || tMin.z() >= aabb.max.z() || tMax.z() <= aabb.min.z())
+ return false;
+
+ Vector center = (aabb.min + aabb.max) * 0.5f;
+ Vector h = aabb.max - center;
+
+ const Vector t[3] { b-a, c-a, c-b };
+
+ Vector ac = a - center;
+
+ Vector n = t[0].cross(t[1]);
+ Scalar s = n.dot(ac);
+ Scalar r = std::abs(h.dot(n.cwiseAbs()));
+ if (abs(s) >= r)
+ return false;
+
+ const Vector at[3] = { t[0].cwiseAbs(), t[1].cwiseAbs(), t[2].cwiseAbs() };
+
+ Vector bc = b - center;
+ Vector cc = c - center;
+
+ // SAT test all cross-axes.
+ // The following is a fully unrolled loop of this code, stored here for reference:
+ /*
+ Scalar d1, d2, a1, a2;
+ const Vector e[3] = { DIR_VEC(1, 0, 0), DIR_VEC(0, 1, 0), DIR_VEC(0, 0, 1) };
+ for(int i = 0; i < 3; ++i)
+ for(int j = 0; j < 3; ++j)
+ {
+ Vector axis = Cross(e[i], t[j]);
+ ProjectToAxis(axis, d1, d2);
+ aabb.ProjectToAxis(axis, a1, a2);
+ if (d2 <= a1 || d1 >= a2) return false;
+ }
+ */
+
+ // eX t[0]
+ Scalar d1 = t[0].y() * ac.z() - t[0].z() * ac.y();
+ Scalar d2 = t[0].y() * cc.z() - t[0].z() * cc.y();
+ Scalar tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.y() * at[0].z() + h.z() * at[0].y());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eX t[1]
+ d1 = t[1].y() * ac.z() - t[1].z() * ac.y();
+ d2 = t[1].y() * bc.z() - t[1].z() * bc.y();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.y() * at[1].z() + h.z() * at[1].y());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eX t[2]
+ d1 = t[2].y() * ac.z() - t[2].z() * ac.y();
+ d2 = t[2].y() * bc.z() - t[2].z() * bc.y();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.y() * at[2].z() + h.z() * at[2].y());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eY t[0]
+ d1 = t[0].z() * ac.x() - t[0].x() * ac.z();
+ d2 = t[0].z() * cc.x() - t[0].x() * cc.z();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.x() * at[0].z() + h.z() * at[0].x());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eY t[1]
+ d1 = t[1].z() * ac.x() - t[1].x() * ac.z();
+ d2 = t[1].z() * bc.x() - t[1].x() * bc.z();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.x() * at[1].z() + h.z() * at[1].x());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eY t[2]
+ d1 = t[2].z() * ac.x() - t[2].x() * ac.z();
+ d2 = t[2].z() * bc.x() - t[2].x() * bc.z();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.x() * at[2].z() + h.z() * at[2].x());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eZ t[0]
+ d1 = t[0].x() * ac.y() - t[0].y() * ac.x();
+ d2 = t[0].x() * cc.y() - t[0].y() * cc.x();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.y() * at[0].x() + h.x() * at[0].y());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eZ t[1]
+ d1 = t[1].x() * ac.y() - t[1].y() * ac.x();
+ d2 = t[1].x() * bc.y() - t[1].y() * bc.x();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.y() * at[1].x() + h.x() * at[1].y());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // eZ t[2]
+ d1 = t[2].x() * ac.y() - t[2].y() * ac.x();
+ d2 = t[2].x() * bc.y() - t[2].y() * bc.x();
+ tc = (d1 + d2) * 0.5f;
+ r = std::abs(h.y() * at[2].x() + h.x() * at[2].y());
+ if (r + std::abs(tc - d1) < std::abs(tc))
+ return false;
+
+ // No separating axis exists, the AABB and triangle intersect.
+ return true;
+}
+
+static double dist2_to_triangle(const Vec3d &a, const Vec3d &b, const Vec3d &c, const Vec3d &p)
+{
+ double out = std::numeric_limits::max();
+ const Vec3d v1 = b - a;
+ auto l1 = v1.squaredNorm();
+ const Vec3d v2 = c - b;
+ auto l2 = v2.squaredNorm();
+ const Vec3d v3 = a - c;
+ auto l3 = v3.squaredNorm();
+
+ // Is the triangle valid?
+ if (l1 > 0. && l2 > 0. && l3 > 0.)
+ {
+ // 1) Project point into the plane of the triangle.
+ const Vec3d n = v1.cross(v2);
+ double d = (p - a).dot(n);
+ const Vec3d foot_pt = p - n * d / n.squaredNorm();
+
+ // 2) Maximum projection of n.
+ int proj_axis;
+ n.array().cwiseAbs().maxCoeff(&proj_axis);
+
+ // 3) Test whether the foot_pt is inside the triangle.
+ {
+ auto inside_triangle = [](const Vec2d& v1, const Vec2d& v2, const Vec2d& v3, const Vec2d& pt) {
+ const double d1 = cross2(v1, pt);
+ const double d2 = cross2(v2, pt);
+ const double d3 = cross2(v3, pt);
+ // Testing both CCW and CW orientations.
+ return (d1 >= 0. && d2 >= 0. && d3 >= 0.) || (d1 <= 0. && d2 <= 0. && d3 <= 0.);
+ };
+ bool inside;
+ switch (proj_axis) {
+ case 0:
+ inside = inside_triangle({v1.y(), v1.z()}, {v2.y(), v2.z()}, {v3.y(), v3.z()}, {foot_pt.y(), foot_pt.z()}); break;
+ case 1:
+ inside = inside_triangle({v1.z(), v1.x()}, {v2.z(), v2.x()}, {v3.z(), v3.x()}, {foot_pt.z(), foot_pt.x()}); break;
+ default:
+ assert(proj_axis == 2);
+ inside = inside_triangle({v1.x(), v1.y()}, {v2.x(), v2.y()}, {v3.x(), v3.y()}, {foot_pt.x(), foot_pt.y()}); break;
+ }
+ if (inside)
+ return (p - foot_pt).squaredNorm();
+ }
+
+ // 4) Find minimum distance to triangle vertices and edges.
+ out = std::min((p - a).squaredNorm(), std::min((p - b).squaredNorm(), (p - c).squaredNorm()));
+ auto t = (p - a).dot(v1);
+ if (t > 0. && t < l1)
+ out = std::min(out, (a + v1 * (t / l1) - p).squaredNorm());
+ t = (p - b).dot(v2);
+ if (t > 0. && t < l2)
+ out = std::min(out, (b + v2 * (t / l2) - p).squaredNorm());
+ t = (p - c).dot(v3);
+ if (t > 0. && t < l3)
+ out = std::min(out, (c + v3 * (t / l3) - p).squaredNorm());
+ }
+
+ return out;
+}
+
+// Ordering of children cubes.
+static const std::array child_centers {
+ Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
+ Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
+};
+
+// Traversal order of octree children cells for three infill directions,
+// so that a single line will be discretized in a strictly monotonous order.
+static constexpr std::array, 3> child_traversal_order {
+ std::array{ 2, 3, 0, 1, 6, 7, 4, 5 },
+ std::array{ 4, 0, 6, 2, 5, 1, 7, 3 },
+ std::array{ 1, 5, 0, 4, 3, 7, 2, 6 },
+};
+
+struct Cube
+{
+ Vec3d center;
+#ifndef NDEBUG
+ Vec3d center_octree;
+#endif // NDEBUG
+ std::array children {}; // initialized to nullptrs
+ Cube(const Vec3d ¢er) : center(center) {}
+};
+
+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 Octree
+{
+ // Octree will allocate its Cubes from the pool. The pool only supports deletion of the complete pool,
+ // perfect for building up our octree.
+ boost::object_pool pool;
+ Cube* root_cube { nullptr };
+ Vec3d origin;
+ std::vector cubes_properties;
+
+ Octree(const Vec3d &origin, const std::vector &cubes_properties)
+ : root_cube(pool.construct(origin)), origin(origin), cubes_properties(cubes_properties) {}
+
+ void insert_triangle(const Vec3d &a, const Vec3d &b, const Vec3d &c, Cube *current_cube, const BoundingBoxf3 ¤t_bbox, int depth);
+};
+
+void OctreeDeleter::operator()(Octree *p) {
+ delete p;
+}
std::pair adaptive_fill_line_spacing(const PrintObject &print_object)
{
@@ -90,330 +344,285 @@ std::pair adaptive_fill_line_spacing(const PrintObject &print_ob
return std::make_pair(adaptive_line_spacing, support_line_spacing);
}
-void FillAdaptive::_fill_surface_single(const FillParams & params,
- unsigned int thickness_layers,
- const std::pair &direction,
- ExPolygon & expolygon,
- Polylines & polylines_out)
+// Context used by generate_infill_lines() when recursively traversing an octree in a DDA fashion
+// (Digital Differential Analyzer).
+struct FillContext
{
- if(this->adapt_fill_octree != nullptr)
- this->generate_infill(params, thickness_layers, direction, expolygon, polylines_out, this->adapt_fill_octree);
-}
-
-void FillAdaptive::generate_infill(const FillParams & params,
- unsigned int thickness_layers,
- const std::pair &direction,
- ExPolygon & expolygon,
- Polylines & polylines_out,
- FillAdaptive_Internal::Octree *octree)
-{
- 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());
-
- // Store grouped lines by its direction (multiple of 120°)
- std::vector infill_lines_dir(3);
- this->generate_infill_lines(octree->root_cube.get(),
- this->z, octree->origin, rotation_matrix,
- infill_lines_dir, octree->cubes_properties,
- int(octree->cubes_properties.size()) - 1);
-
- Polylines all_polylines;
- all_polylines.reserve(infill_lines_dir[0].size() * 3);
- for (Lines &infill_lines : infill_lines_dir)
- {
- for (const Line &line : infill_lines)
- {
- all_polylines.emplace_back(line.a, line.b);
- }
- }
-
- if (params.dont_connect)
- {
- // Crop all polylines
- polylines_out = intersection_pl(all_polylines, to_polygons(expolygon));
- }
- else
- {
- // Crop all polylines
- all_polylines = intersection_pl(all_polylines, to_polygons(expolygon));
-
- Polylines boundary_polylines;
- Polylines non_boundary_polylines;
- for (const Polyline &polyline : all_polylines)
- {
- // connect_infill required all polylines to touch the boundary.
- if(polyline.lines().size() == 1 && expolygon.has_boundary_point(polyline.lines().front().a) && expolygon.has_boundary_point(polyline.lines().front().b))
- {
- boundary_polylines.push_back(polyline);
- }
- else
- {
- non_boundary_polylines.push_back(polyline);
- }
- }
-
- if(!boundary_polylines.empty())
- {
- boundary_polylines = chain_polylines(boundary_polylines);
- FillAdaptive::connect_infill(std::move(boundary_polylines), expolygon, polylines_out, this->spacing, params);
- }
-
- polylines_out.insert(polylines_out.end(), non_boundary_polylines.begin(), non_boundary_polylines.end());
- }
-
-#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
- {
- static int iRuna = 0;
- BoundingBox bbox_svg = this->bounding_box;
- {
- ::Slic3r::SVG svg(debug_out_path("FillAdaptive-%d.svg", iRuna), bbox_svg);
- for (const Polyline &polyline : polylines_out)
- {
- for (const Line &line : polyline.lines())
- {
- Point from = line.a;
- Point to = line.b;
- Point diff = to - from;
-
- float shrink_length = scale_(0.4);
- float line_slope = (float)diff.y() / diff.x();
- float shrink_x = shrink_length / (float)std::sqrt(1.0 + (line_slope * line_slope));
- float shrink_y = line_slope * shrink_x;
-
- to.x() -= shrink_x;
- to.y() -= shrink_y;
- from.x() += shrink_x;
- from.y() += shrink_y;
-
- svg.draw(Line(from, to));
- }
- }
- }
-
- iRuna++;
- }
-#endif /* SLIC3R_DEBUG */
-}
-
-void FillAdaptive::generate_infill_lines(
- FillAdaptive_Internal::Cube *cube,
- double z_position,
- const Vec3d &origin,
- const Transform3d &rotation_matrix,
- std::vector &dir_lines_out,
- const std::vector &cubes_properties,
- int depth)
-{
- using namespace FillAdaptive_Internal;
-
- if(cube == nullptr)
- {
- return;
- }
-
- Vec3d cube_center_tranformed = rotation_matrix * cube->center;
- double z_diff = std::abs(z_position - cube_center_tranformed.z());
-
- if (z_diff > cubes_properties[depth].height / 2)
- {
- return;
- }
-
- if (z_diff < cubes_properties[depth].line_z_distance)
- {
- Point from(
- scale_((cubes_properties[depth].diagonal_length / 2) * (cubes_properties[depth].line_z_distance - z_diff) / cubes_properties[depth].line_z_distance),
- scale_(cubes_properties[depth].line_xy_distance - ((z_position - (cube_center_tranformed.z() - cubes_properties[depth].line_z_distance)) / sqrt(2))));
- Point to(-from.x(), from.y());
- // Relative to cube center
-
- double rotation_angle = (2.0 * M_PI) / 3.0;
- for (Lines &lines : dir_lines_out)
- {
- Vec3d offset = cube_center_tranformed - (rotation_matrix * origin);
- Point from_abs(from), to_abs(to);
-
- from_abs.x() += int(scale_(offset.x()));
- from_abs.y() += int(scale_(offset.y()));
- to_abs.x() += int(scale_(offset.x()));
- to_abs.y() += int(scale_(offset.y()));
-
-// lines.emplace_back(from_abs, to_abs);
- this->connect_lines(lines, Line(from_abs, to_abs));
-
- from.rotate(rotation_angle);
- to.rotate(rotation_angle);
- }
- }
-
- for(const std::unique_ptr &child : cube->children)
- {
- if(child != nullptr)
- {
- generate_infill_lines(child.get(), z_position, origin, rotation_matrix, dir_lines_out, cubes_properties, depth - 1);
- }
- }
-}
-
-void FillAdaptive::connect_lines(Lines &lines, Line new_line)
-{
- auto eps = int(scale_(0.10));
- for (size_t i = 0; i < lines.size(); ++i)
- {
- if (std::abs(new_line.a.x() - lines[i].b.x()) < eps && std::abs(new_line.a.y() - lines[i].b.y()) < eps)
- {
- new_line.a = lines[i].a;
- lines.erase(lines.begin() + i);
- --i;
- continue;
- }
-
- if (std::abs(new_line.b.x() - lines[i].a.x()) < eps && std::abs(new_line.b.y() - lines[i].a.y()) < eps)
- {
- new_line.b = lines[i].b;
- lines.erase(lines.begin() + i);
- --i;
- continue;
- }
- }
-
- lines.emplace_back(new_line.a, new_line.b);
-}
-
-std::unique_ptr FillAdaptive::build_octree(
- TriangleMesh &triangle_mesh,
- coordf_t line_spacing,
- const Vec3d &cube_center)
-{
- using namespace FillAdaptive_Internal;
-
- if(line_spacing <= 0 || std::isnan(line_spacing))
- {
- return nullptr;
- }
-
- Vec3d bb_size = triangle_mesh.bounding_box().size();
- // The furthest point from the center of the bottom of the mesh bounding box.
- double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) +
- ((bb_size.y() * bb_size.y()) / 4.0) +
- (bb_size.z() * bb_size.z()));
- double max_cube_edge_length = furthest_point * 2;
-
- std::vector cubes_properties;
- for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2)
- {
- CubeProperties props{};
- props.edge_length = edge_length;
- props.height = edge_length * sqrt(3);
- props.diagonal_length = edge_length * sqrt(2);
- props.line_z_distance = edge_length / sqrt(3);
- props.line_xy_distance = edge_length / sqrt(6);
- cubes_properties.push_back(props);
- }
-
- if (triangle_mesh.its.vertices.empty())
- {
- triangle_mesh.require_shared_vertices();
- }
-
- AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
- triangle_mesh.its.vertices, triangle_mesh.its.indices);
- auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties);
-
- FillAdaptive::expand_cube(octree->root_cube.get(), cubes_properties, aabbTree, triangle_mesh, int(cubes_properties.size()) - 1);
-
- return octree;
-}
-
-void FillAdaptive::expand_cube(
- FillAdaptive_Internal::Cube *cube,
- const std::vector &cubes_properties,
- const AABBTreeIndirect::Tree3f &distance_tree,
- const TriangleMesh &triangle_mesh, int depth)
-{
- using namespace FillAdaptive_Internal;
-
- if (cube == nullptr || depth == 0)
- {
- return;
- }
-
- std::vector child_centers = {
- Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
- Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
+ // The angles have to agree with child_traversal_order.
+ static constexpr double direction_angles[3] {
+ 0.,
+ (2.0 * M_PI) / 3.0,
+ -(2.0 * M_PI) / 3.0
};
- double cube_radius_squared = (cubes_properties[depth].height * cubes_properties[depth].height) / 16;
-
- for (size_t i = 0; i < 8; ++i)
+ FillContext(const Octree &octree, double z_position, int direction_idx) :
+ cubes_properties(octree.cubes_properties),
+ z_position(z_position),
+ traversal_order(child_traversal_order[direction_idx]),
+ cos_a(cos(direction_angles[direction_idx])),
+ sin_a(sin(direction_angles[direction_idx]))
{
- const Vec3d &child_center = child_centers[i];
- Vec3d child_center_transformed = cube->center + (child_center * (cubes_properties[depth].edge_length / 4));
-
- if(AABBTreeIndirect::is_any_triangle_in_radius(triangle_mesh.its.vertices, triangle_mesh.its.indices,
- distance_tree, child_center_transformed, cube_radius_squared))
- {
- cube->children[i] = std::make_unique(child_center_transformed);
- FillAdaptive::expand_cube(cube->children[i].get(), cubes_properties, distance_tree, triangle_mesh, depth - 1);
- }
+ static constexpr auto unused = std::numeric_limits::max();
+ temp_lines.assign((1 << octree.cubes_properties.size()) - 1, Line(Point(unused, unused), Point(unused, unused)));
}
+
+ // Rotate the point, uses the same convention as Point::rotate().
+ Vec2d rotate(const Vec2d& v) { return Vec2d(this->cos_a * v.x() - this->sin_a * v.y(), this->sin_a * v.x() + this->cos_a * v.y()); }
+
+ const std::vector &cubes_properties;
+ // Top of the current layer.
+ const double z_position;
+ // Order of traversal for this line direction.
+ const std::array traversal_order;
+ // Rotation of the generated line for this line direction.
+ const double cos_a;
+ const double sin_a;
+
+ // Linearized tree spanning a single Octree wall, used to connect lines spanning
+ // neighboring Octree cells. Unused lines have the Line::a::x set to infinity.
+ std::vector temp_lines;
+ // Final output
+ std::vector output_lines;
+};
+
+static constexpr double octree_rot[3] = { 5.0 * M_PI / 4.0, Geometry::deg2rad(215.264), M_PI / 6.0 };
+
+Eigen::Quaterniond transform_to_world()
+{
+ return Eigen::AngleAxisd(octree_rot[2], Vec3d::UnitZ()) * Eigen::AngleAxisd(octree_rot[1], Vec3d::UnitY()) * Eigen::AngleAxisd(octree_rot[0], Vec3d::UnitX());
}
-void FillAdaptive_Internal::Octree::propagate_point(
- Vec3d point,
- FillAdaptive_Internal::Cube * current,
- int depth,
- const std::vector &cubes_properties)
+Eigen::Quaterniond transform_to_octree()
{
- using namespace FillAdaptive_Internal;
+ return Eigen::AngleAxisd(- octree_rot[0], Vec3d::UnitX()) * Eigen::AngleAxisd(- octree_rot[1], Vec3d::UnitY()) * Eigen::AngleAxisd(- octree_rot[2], Vec3d::UnitZ());
+}
- if(depth <= 0)
- {
+#ifndef NDEBUG
+// Verify that the traversal order of the octree children matches the line direction,
+// therefore the infill line may get extended with O(1) time & space complexity.
+static bool verify_traversal_order(
+ FillContext &context,
+ const Cube *cube,
+ int depth,
+ const Vec2d &line_from,
+ const Vec2d &line_to)
+{
+ std::array c;
+ Eigen::Quaterniond to_world = transform_to_world();
+ for (int i = 0; i < 8; ++i) {
+ int j = context.traversal_order[i];
+ Vec3d cntr = to_world * (cube->center_octree + (child_centers[j] * (context.cubes_properties[depth].edge_length / 4.)));
+ assert(!cube->children[j] || cube->children[j]->center.isApprox(cntr));
+ c[i] = cntr;
+ }
+ std::array dirs = {
+ c[1] - c[0], c[2] - c[0], c[3] - c[1], c[3] - c[2], c[3] - c[0],
+ c[5] - c[4], c[6] - c[4], c[7] - c[5], c[7] - c[6], c[7] - c[4]
+ };
+ assert(std::abs(dirs[4].z()) < 0.005);
+ assert(std::abs(dirs[9].z()) < 0.005);
+ assert(dirs[0].isApprox(dirs[3]));
+ assert(dirs[1].isApprox(dirs[2]));
+ assert(dirs[5].isApprox(dirs[8]));
+ assert(dirs[6].isApprox(dirs[7]));
+ Vec3d line_dir = Vec3d(line_to.x() - line_from.x(), line_to.y() - line_from.y(), 0.).normalized();
+ for (auto& dir : dirs) {
+ double d = dir.normalized().dot(line_dir);
+ assert(d > 0.7);
+ }
+ return true;
+}
+#endif // NDEBUG
+
+static void generate_infill_lines_recursive(
+ FillContext &context,
+ const Cube *cube,
+ // Address of this wall in the octree, used to address context.temp_lines.
+ int address,
+ int depth)
+{
+ assert(cube != nullptr);
+
+ const std::vector &cubes_properties = context.cubes_properties;
+ const double z_diff = context.z_position - cube->center.z();
+ const double z_diff_abs = std::abs(z_diff);
+
+ if (z_diff_abs > cubes_properties[depth].height / 2.)
return;
+
+ if (z_diff_abs < cubes_properties[depth].line_z_distance) {
+ // Discretize a single wall splitting the cube into two.
+ const double zdist = cubes_properties[depth].line_z_distance;
+ Vec2d from(
+ 0.5 * cubes_properties[depth].diagonal_length * (zdist - z_diff_abs) / zdist,
+ cubes_properties[depth].line_xy_distance - (zdist + z_diff) / sqrt(2.));
+ Vec2d to(-from.x(), from.y());
+ from = context.rotate(from);
+ to = context.rotate(to);
+ // Relative to cube center
+ const Vec2d offset(cube->center.x(), cube->center.y());
+ from += offset;
+ to += offset;
+ // Verify that the traversal order of the octree children matches the line direction,
+ // therefore the infill line may get extended with O(1) time & space complexity.
+ assert(verify_traversal_order(context, cube, depth, from, to));
+ // Either extend an existing line or start a new one.
+ Line &last_line = context.temp_lines[address];
+ Line new_line(Point::new_scale(from), Point::new_scale(to));
+ if (last_line.a.x() == std::numeric_limits::max()) {
+ last_line.a = new_line.a;
+ } else if ((new_line.a - last_line.b).cwiseAbs().maxCoeff() > 300) { // SCALED_EPSILON is 100 and it is not enough
+ context.output_lines.emplace_back(last_line);
+ last_line.a = new_line.a;
+ }
+ last_line.b = new_line.b;
}
- size_t octant_idx = Octree::find_octant(point, current->center);
- Cube * child = current->children[octant_idx].get();
-
- // Octant not exists, then create it
- if(child == nullptr) {
- std::vector child_centers = {
- Vec3d(-1, -1, -1), Vec3d( 1, -1, -1), Vec3d(-1, 1, -1), Vec3d( 1, 1, -1),
- Vec3d(-1, -1, 1), Vec3d( 1, -1, 1), Vec3d(-1, 1, 1), Vec3d( 1, 1, 1)
- };
-
- const Vec3d &child_center = child_centers[octant_idx];
- Vec3d child_center_transformed = current->center + (child_center * (cubes_properties[depth].edge_length / 4));
-
- current->children[octant_idx] = std::make_unique(child_center_transformed);
- child = current->children[octant_idx].get();
+ // left child index
+ address = address * 2 + 1;
+ -- depth;
+ size_t i = 0;
+ for (const int child_idx : context.traversal_order) {
+ const Cube *child = cube->children[child_idx];
+ if (child != nullptr)
+ generate_infill_lines_recursive(context, child, address, depth);
+ if (++ i == 4)
+ // right child index
+ ++ address;
}
-
- Octree::propagate_point(point, child, (depth - 1), cubes_properties);
}
-std::unique_ptr FillSupportCubic::build_octree(
- TriangleMesh & triangle_mesh,
- coordf_t line_spacing,
- const Vec3d & cube_center,
- const Transform3d &rotation_matrix)
-{
- using namespace FillAdaptive_Internal;
+#ifndef NDEBUG
+// #define ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
+#endif
- if(line_spacing <= 0 || std::isnan(line_spacing))
+#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
+static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines &polylines, const std::string &path)
+{
+ BoundingBox bbox = get_extents(expoly);
+ bbox.offset(scale_(3.));
+
+ ::Slic3r::SVG svg(path, bbox);
+ svg.draw(expoly);
+ svg.draw_outline(expoly, "green");
+ svg.draw(polylines, "red");
+ static constexpr double trim_length = scale_(0.4);
+ for (Polyline polyline : polylines) {
+ Vec2d a = polyline.points.front().cast();
+ Vec2d d = polyline.points.back().cast();
+ if (polyline.size() == 2) {
+ Vec2d v = d - a;
+ double l = v.norm();
+ if (l > 2. * trim_length) {
+ a += v * trim_length / l;
+ d -= v * trim_length / l;
+ polyline.points.front() = a.cast();
+ polyline.points.back() = d.cast();
+ } else
+ polyline.points.clear();
+ } else if (polyline.size() > 2) {
+ Vec2d b = polyline.points[1].cast();
+ Vec2d c = polyline.points[polyline.points.size() - 2].cast();
+ Vec2d v = b - a;
+ double l = v.norm();
+ if (l > trim_length) {
+ a += v * trim_length / l;
+ polyline.points.front() = a.cast();
+ } else
+ polyline.points.erase(polyline.points.begin());
+ v = d - c;
+ l = v.norm();
+ if (l > trim_length)
+ polyline.points.back() = (d - v * trim_length / l).cast();
+ else
+ polyline.points.pop_back();
+ }
+ svg.draw(polyline, "black");
+ }
+}
+#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
+
+void Filler::_fill_surface_single(
+ const FillParams & params,
+ unsigned int thickness_layers,
+ const std::pair &direction,
+ ExPolygon &expolygon,
+ Polylines &polylines_out)
+{
+ assert (this->adapt_fill_octree);
+
+ Polylines all_polylines;
{
- return nullptr;
+ // 3 contexts for three directions of infill lines
+ std::array contexts {
+ FillContext { *adapt_fill_octree, this->z, 0 },
+ FillContext { *adapt_fill_octree, this->z, 1 },
+ FillContext { *adapt_fill_octree, this->z, 2 }
+ };
+ // Generate the infill lines along the octree cells, merge touching lines of the same direction.
+ size_t num_lines = 0;
+ for (auto &context : contexts) {
+ generate_infill_lines_recursive(context, adapt_fill_octree->root_cube, 0, int(adapt_fill_octree->cubes_properties.size()) - 1);
+ num_lines += context.output_lines.size() + context.temp_lines.size();
+ }
+ // Collect the lines.
+ std::vector lines;
+ lines.reserve(num_lines);
+ for (auto &context : contexts) {
+ append(lines, context.output_lines);
+ for (const Line &line : context.temp_lines)
+ if (line.a.x() != std::numeric_limits::max())
+ lines.emplace_back(line);
+ }
+#if 0
+ // Chain touching line segments, convert lines to polylines.
+ //all_polylines = chain_lines(lines, 300.); // SCALED_EPSILON is 100 and it is not enough
+#else
+ // Convert lines to polylines.
+ all_polylines.reserve(lines.size());
+ std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
+#endif
}
- Vec3d bb_size = triangle_mesh.bounding_box().size();
- // The furthest point from the center of the bottom of the mesh bounding box.
- double furthest_point = std::sqrt(((bb_size.x() * bb_size.x()) / 4.0) +
- ((bb_size.y() * bb_size.y()) / 4.0) +
- (bb_size.z() * bb_size.z()));
- double max_cube_edge_length = furthest_point * 2;
+ // Crop all polylines
+ all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
+
+#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
+ {
+ static int iRun = 0;
+ export_infill_lines_to_svg(expolygon, all_polylines, debug_out_path("FillAdaptive-initial-%d.svg", iRun++));
+ }
+#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
+
+ if (params.dont_connect || all_polylines.size() <= 1)
+ append(polylines_out, std::move(all_polylines));
+ else
+ connect_infill(chain_polylines(std::move(all_polylines)), expolygon, polylines_out, this->spacing, params);
+
+#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
+ {
+ static int iRun = 0;
+ export_infill_lines_to_svg(expolygon, polylines_out, debug_out_path("FillAdaptive-final-%d.svg", iRun ++));
+ }
+#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
+}
+
+static double bbox_max_radius(const BoundingBoxf3 &bbox, const Vec3d ¢er)
+{
+ const auto p = (bbox.min - center);
+ const auto s = bbox.size();
+ double r2max = 0.;
+ for (int i = 0; i < 8; ++ i)
+ r2max = std::max(r2max, (p + Vec3d(s.x() * double(i & 1), s.y() * double(i & 2), s.z() * double(i & 4))).squaredNorm());
+ return sqrt(r2max);
+}
+
+static std::vector make_cubes_properties(double max_cube_edge_length, double line_spacing)
+{
+ max_cube_edge_length += EPSILON;
std::vector cubes_properties;
- for (double edge_length = (line_spacing * 2); edge_length < (max_cube_edge_length * 2); edge_length *= 2)
+ for (double edge_length = line_spacing * 2.;; edge_length *= 2.)
{
CubeProperties props{};
props.edge_length = edge_length;
@@ -421,100 +630,113 @@ std::unique_ptr FillSupportCubic::build_octree(
props.diagonal_length = edge_length * sqrt(2);
props.line_z_distance = edge_length / sqrt(3);
props.line_xy_distance = edge_length / sqrt(6);
- cubes_properties.push_back(props);
+ cubes_properties.emplace_back(props);
+ if (edge_length > max_cube_edge_length)
+ break;
}
+ return cubes_properties;
+}
- if (triangle_mesh.its.vertices.empty())
- {
- triangle_mesh.require_shared_vertices();
- }
+static inline bool is_overhang_triangle(const Vec3d &a, const Vec3d &b, const Vec3d &c, const Vec3d &up)
+{
+ // Calculate triangle normal.
+ auto n = (b - a).cross(c - b);
+ return n.dot(up) > 0.707 * n.norm();
+}
- AABBTreeIndirect::Tree3f aabbTree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
- triangle_mesh.its.vertices, triangle_mesh.its.indices);
+static void transform_center(Cube *current_cube, const Eigen::Matrix3d &rot)
+{
+#ifndef NDEBUG
+ current_cube->center_octree = current_cube->center;
+#endif // NDEBUG
+ current_cube->center = rot * current_cube->center;
+ for (auto *child : current_cube->children)
+ if (child)
+ transform_center(child, rot);
+}
- auto octree = std::make_unique(std::make_unique(cube_center), cube_center, cubes_properties);
+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 &overhang_triangles,
+ coordf_t line_spacing,
+ bool support_overhangs_only)
+{
+ assert(line_spacing > 0);
+ assert(! std::isnan(line_spacing));
- double cube_edge_length = line_spacing / 2.0;
- int max_depth = int(octree->cubes_properties.size()) - 1;
- BoundingBoxf3 mesh_bb = triangle_mesh.bounding_box();
- Vec3f vertical(0, 0, 1);
+ BoundingBox3Base bbox(triangle_mesh.vertices);
+ Vec3d cube_center = bbox.center().cast();
+ std::vector cubes_properties = make_cubes_properties(double(bbox.size().maxCoeff()), line_spacing);
+ auto octree = OctreePtr(new Octree(cube_center, cubes_properties));
- for (size_t facet_idx = 0; facet_idx < triangle_mesh.stl.facet_start.size(); ++facet_idx)
- {
- if(triangle_mesh.stl.facet_start[facet_idx].normal.dot(vertical) <= 0.707)
- {
- // The angle is smaller than PI/4, than infill don't to be there
- continue;
+ if (cubes_properties.size() > 1) {
+ Octree *octree_ptr = octree.get();
+ double edge_length_half = 0.5 * cubes_properties.back().edge_length;
+ Vec3d diag_half(edge_length_half, edge_length_half, edge_length_half);
+ int max_depth = int(cubes_properties.size()) - 1;
+ auto process_triangle = [octree_ptr, max_depth, diag_half](const Vec3d &a, const Vec3d &b, const Vec3d &c) {
+ octree_ptr->insert_triangle(
+ a, b, c,
+ octree_ptr->root_cube,
+ BoundingBoxf3(octree_ptr->root_cube->center - diag_half, octree_ptr->root_cube->center + diag_half),
+ max_depth);
+ };
+ auto up_vector = support_overhangs_only ? Vec3d(transform_to_octree() * Vec3d(0., 0., 1.)) : Vec3d();
+ for (auto &tri : triangle_mesh.indices) {
+ auto a = triangle_mesh.vertices[tri[0]].cast();
+ auto b = triangle_mesh.vertices[tri[1]].cast();
+ auto c = triangle_mesh.vertices[tri[2]].cast();
+ if (! support_overhangs_only || is_overhang_triangle(a, b, c, up_vector))
+ process_triangle(a, b, c);
}
-
- stl_vertex v_1 = triangle_mesh.stl.facet_start[facet_idx].vertex[0];
- stl_vertex v_2 = triangle_mesh.stl.facet_start[facet_idx].vertex[1];
- stl_vertex v_3 = triangle_mesh.stl.facet_start[facet_idx].vertex[2];
-
- std::vector triangle_vertices =
- {Vec3d(v_1.x(), v_1.y(), v_1.z()),
- Vec3d(v_2.x(), v_2.y(), v_2.z()),
- Vec3d(v_3.x(), v_3.y(), v_3.z())};
-
- BoundingBoxf3 triangle_bb(triangle_vertices);
-
- Vec3d triangle_start_relative = triangle_bb.min - mesh_bb.min;
- Vec3d triangle_end_relative = triangle_bb.max - mesh_bb.min;
-
- Vec3crd triangle_start_idx = Vec3crd(
- int(std::floor(triangle_start_relative.x() / cube_edge_length)),
- int(std::floor(triangle_start_relative.y() / cube_edge_length)),
- int(std::floor(triangle_start_relative.z() / cube_edge_length)));
- Vec3crd triangle_end_idx = Vec3crd(
- int(std::floor(triangle_end_relative.x() / cube_edge_length)),
- int(std::floor(triangle_end_relative.y() / cube_edge_length)),
- int(std::floor(triangle_end_relative.z() / cube_edge_length)));
-
- for (int z = triangle_start_idx.z(); z <= triangle_end_idx.z(); ++z)
+ for (size_t i = 0; i < overhang_triangles.size(); i += 3)
+ process_triangle(overhang_triangles[i], overhang_triangles[i + 1], overhang_triangles[i + 2]);
{
- for (int y = triangle_start_idx.y(); y <= triangle_end_idx.y(); ++y)
- {
- for (int x = triangle_start_idx.x(); x <= triangle_end_idx.x(); ++x)
- {
- Vec3d cube_center_relative(x * cube_edge_length + (cube_edge_length / 2.0), y * cube_edge_length + (cube_edge_length / 2.0), z * cube_edge_length);
- Vec3d cube_center_absolute = cube_center_relative + mesh_bb.min;
-
- double cube_center_absolute_arr[3] = {cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z()};
- double distance = 0, cord_u = 0, cord_v = 0;
-
- double dir[3] = {0.0, 0.0, 1.0};
-
- double vert_0[3] = {triangle_vertices[0].x(),
- triangle_vertices[0].y(),
- triangle_vertices[0].z()};
- double vert_1[3] = {triangle_vertices[1].x(),
- triangle_vertices[1].y(),
- triangle_vertices[1].z()};
- double vert_2[3] = {triangle_vertices[2].x(),
- triangle_vertices[2].y(),
- triangle_vertices[2].z()};
-
- if(intersect_triangle(cube_center_absolute_arr, dir, vert_0, vert_1, vert_2, &distance, &cord_u, &cord_v) && distance > 0 && distance <= cube_edge_length)
- {
- Vec3d cube_center_transformed(cube_center_absolute.x(), cube_center_absolute.y(), cube_center_absolute.z() + (cube_edge_length / 2.0));
- Octree::propagate_point(rotation_matrix * cube_center_transformed, octree->root_cube.get(), max_depth, octree->cubes_properties);
- }
- }
- }
+ // Transform the octree to world coordinates to reduce computation when extracting infill lines.
+ auto rot = transform_to_world().toRotationMatrix();
+ transform_center(octree->root_cube, rot);
+ octree->origin = rot * octree->origin;
}
}
return octree;
}
-void FillSupportCubic::_fill_surface_single(const FillParams & params,
- unsigned int thickness_layers,
- const std::pair &direction,
- ExPolygon & expolygon,
- Polylines & polylines_out)
+void Octree::insert_triangle(const Vec3d &a, const Vec3d &b, const Vec3d &c, Cube *current_cube, const BoundingBoxf3 ¤t_bbox, int depth)
{
- if (this->support_fill_octree != nullptr)
- this->generate_infill(params, thickness_layers, direction, expolygon, polylines_out, this->support_fill_octree);
+ assert(current_cube);
+ assert(depth > 0);
+
+ // Squared radius of a sphere around the child cube.
+ const double r2_cube = Slic3r::sqr(0.5 * this->cubes_properties[-- depth].height + EPSILON);
+
+ for (size_t i = 0; i < 8; ++ i) {
+ const Vec3d &child_center_dir = child_centers[i];
+ // Calculate a slightly expanded bounding box of a child cube to cope with triangles touching a cube wall and other numeric errors.
+ // We will rather densify the octree a bit more than necessary instead of missing a triangle.
+ BoundingBoxf3 bbox;
+ for (int k = 0; k < 3; ++ k) {
+ if (child_center_dir[k] == -1.) {
+ bbox.min[k] = current_bbox.min[k];
+ bbox.max[k] = current_cube->center[k] + EPSILON;
+ } else {
+ bbox.min[k] = current_cube->center[k] - EPSILON;
+ bbox.max[k] = current_bbox.max[k];
+ }
+ }
+ Vec3d child_center = current_cube->center + (child_center_dir * (this->cubes_properties[depth].edge_length / 2.));
+ //if (dist2_to_triangle(a, b, c, child_center) < r2_cube) {
+ if (triangle_AABB_intersects(a, b, c, bbox)) {
+ if (! current_cube->children[i])
+ current_cube->children[i] = this->pool.construct(child_center);
+ if (depth > 0)
+ this->insert_triangle(a, b, c, current_cube->children[i], bbox, depth);
+ }
+ }
}
+} // namespace FillAdaptive
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp
index 4bb80fa063..f10c40b99f 100644
--- a/src/libslic3r/Fill/FillAdaptive.hpp
+++ b/src/libslic3r/Fill/FillAdaptive.hpp
@@ -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 children[8] = {};
- Cube(const Vec3d ¢er) : 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;
- struct Octree
- {
- std::unique_ptr