mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-09-28 07:43:12 +08:00
Merge branch 'lh_shrinkage_compensation'
This commit is contained in:
commit
0cfbb5a437
@ -7,12 +7,12 @@ Painting::Painting(const Transform3d &obj_transform, const ModelVolumePtrs &volu
|
|||||||
auto model_transformation = obj_transform * mv->get_matrix();
|
auto model_transformation = obj_transform * mv->get_matrix();
|
||||||
|
|
||||||
indexed_triangle_set enforcers = mv->seam_facets
|
indexed_triangle_set enforcers = mv->seam_facets
|
||||||
.get_facets(*mv, EnforcerBlockerType::ENFORCER);
|
.get_facets(*mv, TriangleStateType::ENFORCER);
|
||||||
its_transform(enforcers, model_transformation);
|
its_transform(enforcers, model_transformation);
|
||||||
its_merge(this->enforcers, enforcers);
|
its_merge(this->enforcers, enforcers);
|
||||||
|
|
||||||
indexed_triangle_set blockers = mv->seam_facets
|
indexed_triangle_set blockers = mv->seam_facets
|
||||||
.get_facets(*mv, EnforcerBlockerType::BLOCKER);
|
.get_facets(*mv, TriangleStateType::BLOCKER);
|
||||||
its_transform(blockers, model_transformation);
|
its_transform(blockers, model_transformation);
|
||||||
its_merge(this->blockers, blockers);
|
its_merge(this->blockers, blockers);
|
||||||
}
|
}
|
||||||
|
@ -637,6 +637,21 @@ Transform3d Transformation::get_matrix_no_scaling_factor() const
|
|||||||
return copy.get_matrix();
|
return copy.get_matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transform3d Transformation::get_matrix_with_applied_shrinkage_compensation(const Vec3d &shrinkage_compensation) const {
|
||||||
|
const Transform3d shrinkage_trafo = Geometry::scale_transform(shrinkage_compensation);
|
||||||
|
const Vec3d trafo_offset = this->get_offset();
|
||||||
|
const Vec3d trafo_offset_xy = Vec3d(trafo_offset.x(), trafo_offset.y(), 0.);
|
||||||
|
|
||||||
|
Transformation copy(*this);
|
||||||
|
copy.set_offset(Axis::X, 0.);
|
||||||
|
copy.set_offset(Axis::Y, 0.);
|
||||||
|
|
||||||
|
Transform3d trafo_after_shrinkage = (shrinkage_trafo * copy.get_matrix());
|
||||||
|
trafo_after_shrinkage.translation() += trafo_offset_xy;
|
||||||
|
|
||||||
|
return trafo_after_shrinkage;
|
||||||
|
}
|
||||||
|
|
||||||
Transformation Transformation::operator * (const Transformation& other) const
|
Transformation Transformation::operator * (const Transformation& other) const
|
||||||
{
|
{
|
||||||
return Transformation(get_matrix() * other.get_matrix());
|
return Transformation(get_matrix() * other.get_matrix());
|
||||||
|
@ -460,6 +460,8 @@ public:
|
|||||||
Transform3d get_matrix_no_offset() const;
|
Transform3d get_matrix_no_offset() const;
|
||||||
Transform3d get_matrix_no_scaling_factor() const;
|
Transform3d get_matrix_no_scaling_factor() const;
|
||||||
|
|
||||||
|
Transform3d get_matrix_with_applied_shrinkage_compensation(const Vec3d &shrinkage_compensation) const;
|
||||||
|
|
||||||
void set_matrix(const Transform3d& transform) { m_matrix = transform; }
|
void set_matrix(const Transform3d& transform) { m_matrix = transform; }
|
||||||
|
|
||||||
Transformation operator * (const Transformation& other) const;
|
Transformation operator * (const Transformation& other) const;
|
||||||
|
@ -1998,6 +1998,22 @@ void ModelVolume::convert_from_meters()
|
|||||||
this->source.is_converted_from_meters = true;
|
this->source.is_converted_from_meters = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> ModelVolume::get_extruders_from_multi_material_painting() const {
|
||||||
|
if (!this->is_mm_painted())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
assert(static_cast<size_t>(TriangleStateType::Extruder1) - 1 == 0);
|
||||||
|
const TriangleSelector::TriangleSplittingData &data = this->mm_segmentation_facets.get_data();
|
||||||
|
|
||||||
|
std::vector<size_t> extruders;
|
||||||
|
for (size_t state_idx = static_cast<size_t>(TriangleStateType::Extruder1); state_idx < data.used_states.size(); ++state_idx) {
|
||||||
|
if (data.used_states[state_idx])
|
||||||
|
extruders.emplace_back(state_idx - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return extruders;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
|
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
|
||||||
{
|
{
|
||||||
mesh->transform(dont_translate ? get_matrix_no_offset() : get_matrix());
|
mesh->transform(dont_translate ? get_matrix_no_offset() : get_matrix());
|
||||||
@ -2021,7 +2037,7 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
|
|||||||
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
|
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
|
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, TriangleStateType type) const
|
||||||
{
|
{
|
||||||
TriangleSelector selector(mv.mesh());
|
TriangleSelector selector(mv.mesh());
|
||||||
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||||
@ -2029,7 +2045,7 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce
|
|||||||
return selector.get_facets(type);
|
return selector.get_facets(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const
|
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, TriangleStateType type) const
|
||||||
{
|
{
|
||||||
TriangleSelector selector(mv.mesh());
|
TriangleSelector selector(mv.mesh());
|
||||||
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
// Reset of TriangleSelector is done inside TriangleSelector's constructor, so we don't need it to perform it again in deserialize().
|
||||||
@ -2037,14 +2053,14 @@ indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv,
|
|||||||
return selector.get_facets_strict(type);
|
return selector.get_facets_strict(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType type) const
|
bool FacetsAnnotation::has_facets(const ModelVolume& mv, TriangleStateType type) const
|
||||||
{
|
{
|
||||||
return TriangleSelector::has_facets(m_data, type);
|
return TriangleSelector::has_facets(m_data, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
bool FacetsAnnotation::set(const TriangleSelector& selector)
|
||||||
{
|
{
|
||||||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> sel_map = selector.serialize();
|
TriangleSelector::TriangleSplittingData sel_map = selector.serialize();
|
||||||
if (sel_map != m_data) {
|
if (sel_map != m_data) {
|
||||||
m_data = std::move(sel_map);
|
m_data = std::move(sel_map);
|
||||||
this->touch();
|
this->touch();
|
||||||
@ -2055,8 +2071,8 @@ bool FacetsAnnotation::set(const TriangleSelector& selector)
|
|||||||
|
|
||||||
void FacetsAnnotation::reset()
|
void FacetsAnnotation::reset()
|
||||||
{
|
{
|
||||||
m_data.first.clear();
|
m_data.triangles_to_split.clear();
|
||||||
m_data.second.clear();
|
m_data.bitstream.clear();
|
||||||
this->touch();
|
this->touch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2067,15 +2083,15 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
|||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
|
|
||||||
auto triangle_it = std::lower_bound(m_data.first.begin(), m_data.first.end(), triangle_idx, [](const std::pair<int, int> &l, const int r) { return l.first < r; });
|
auto triangle_it = std::lower_bound(m_data.triangles_to_split.begin(), m_data.triangles_to_split.end(), triangle_idx, [](const TriangleSelector::TriangleBitStreamMapping &l, const int r) { return l.triangle_idx < r; });
|
||||||
if (triangle_it != m_data.first.end() && triangle_it->first == triangle_idx) {
|
if (triangle_it != m_data.triangles_to_split.end() && triangle_it->triangle_idx == triangle_idx) {
|
||||||
int offset = triangle_it->second;
|
int offset = triangle_it->bitstream_start_idx;
|
||||||
int end = ++ triangle_it == m_data.first.end() ? int(m_data.second.size()) : triangle_it->second;
|
int end = ++ triangle_it == m_data.triangles_to_split.end() ? int(m_data.bitstream.size()) : triangle_it->bitstream_start_idx;
|
||||||
while (offset < end) {
|
while (offset < end) {
|
||||||
int next_code = 0;
|
int next_code = 0;
|
||||||
for (int i=3; i>=0; --i) {
|
for (int i=3; i>=0; --i) {
|
||||||
next_code = next_code << 1;
|
next_code = next_code << 1;
|
||||||
next_code |= int(m_data.second[offset + i]);
|
next_code |= int(m_data.bitstream[offset + i]);
|
||||||
}
|
}
|
||||||
offset += 4;
|
offset += 4;
|
||||||
|
|
||||||
@ -2092,9 +2108,10 @@ std::string FacetsAnnotation::get_triangle_as_string(int triangle_idx) const
|
|||||||
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
|
void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::string& str)
|
||||||
{
|
{
|
||||||
assert(! str.empty());
|
assert(! str.empty());
|
||||||
assert(m_data.first.empty() || m_data.first.back().first < triangle_id);
|
assert(m_data.triangles_to_split.empty() || m_data.triangles_to_split.back().triangle_idx < triangle_id);
|
||||||
m_data.first.emplace_back(triangle_id, int(m_data.second.size()));
|
m_data.triangles_to_split.emplace_back(triangle_id, int(m_data.bitstream.size()));
|
||||||
|
|
||||||
|
const size_t bitstream_start_idx = m_data.bitstream.size();
|
||||||
for (auto it = str.crbegin(); it != str.crend(); ++it) {
|
for (auto it = str.crbegin(); it != str.crend(); ++it) {
|
||||||
const char ch = *it;
|
const char ch = *it;
|
||||||
int dec = 0;
|
int dec = 0;
|
||||||
@ -2106,9 +2123,11 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
|
|||||||
assert(false);
|
assert(false);
|
||||||
|
|
||||||
// Convert to binary and append into code.
|
// Convert to binary and append into code.
|
||||||
for (int i=0; i<4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
m_data.second.insert(m_data.second.end(), bool(dec & (1 << i)));
|
m_data.bitstream.insert(m_data.bitstream.end(), bool(dec & (1 << i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_data.update_used_states(bitstream_start_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "enum_bitmask.hpp"
|
#include "enum_bitmask.hpp"
|
||||||
#include "TextConfiguration.hpp"
|
#include "TextConfiguration.hpp"
|
||||||
#include "EmbossShape.hpp"
|
#include "EmbossShape.hpp"
|
||||||
|
#include "TriangleSelector.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -56,7 +57,6 @@ class ModelVolume;
|
|||||||
class ModelWipeTower;
|
class ModelWipeTower;
|
||||||
class Print;
|
class Print;
|
||||||
class SLAPrint;
|
class SLAPrint;
|
||||||
class TriangleSelector;
|
|
||||||
|
|
||||||
namespace UndoRedo {
|
namespace UndoRedo {
|
||||||
class StackImpl;
|
class StackImpl;
|
||||||
@ -654,29 +654,6 @@ private:
|
|||||||
void update_min_max_z();
|
void update_min_max_z();
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EnforcerBlockerType : int8_t {
|
|
||||||
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits.
|
|
||||||
NONE = 0,
|
|
||||||
ENFORCER = 1,
|
|
||||||
BLOCKER = 2,
|
|
||||||
// Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code.
|
|
||||||
Extruder1 = ENFORCER,
|
|
||||||
Extruder2 = BLOCKER,
|
|
||||||
Extruder3,
|
|
||||||
Extruder4,
|
|
||||||
Extruder5,
|
|
||||||
Extruder6,
|
|
||||||
Extruder7,
|
|
||||||
Extruder8,
|
|
||||||
Extruder9,
|
|
||||||
Extruder10,
|
|
||||||
Extruder11,
|
|
||||||
Extruder12,
|
|
||||||
Extruder13,
|
|
||||||
Extruder14,
|
|
||||||
Extruder15,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ConversionType : int {
|
enum class ConversionType : int {
|
||||||
CONV_TO_INCH,
|
CONV_TO_INCH,
|
||||||
CONV_FROM_INCH,
|
CONV_FROM_INCH,
|
||||||
@ -687,14 +664,14 @@ enum class ConversionType : int {
|
|||||||
class FacetsAnnotation final : public ObjectWithTimestamp {
|
class FacetsAnnotation final : public ObjectWithTimestamp {
|
||||||
public:
|
public:
|
||||||
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
// Assign the content if the timestamp differs, don't assign an ObjectID.
|
||||||
void assign(const FacetsAnnotation& rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
void assign(const FacetsAnnotation &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
||||||
void assign(FacetsAnnotation&& rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
void assign(FacetsAnnotation &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
||||||
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { return m_data; }
|
const TriangleSelector::TriangleSplittingData &get_data() const noexcept { return m_data; }
|
||||||
bool set(const TriangleSelector& selector);
|
bool set(const TriangleSelector& selector);
|
||||||
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
indexed_triangle_set get_facets(const ModelVolume& mv, TriangleStateType type) const;
|
||||||
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
|
indexed_triangle_set get_facets_strict(const ModelVolume& mv, TriangleStateType type) const;
|
||||||
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
bool has_facets(const ModelVolume& mv, TriangleStateType type) const;
|
||||||
bool empty() const { return m_data.first.empty(); }
|
bool empty() const { return m_data.triangles_to_split.empty(); }
|
||||||
|
|
||||||
// Following method clears the config and increases its timestamp, so the deleted
|
// Following method clears the config and increases its timestamp, so the deleted
|
||||||
// state is considered changed from perspective of the undo/redo stack.
|
// state is considered changed from perspective of the undo/redo stack.
|
||||||
@ -704,11 +681,11 @@ public:
|
|||||||
std::string get_triangle_as_string(int i) const;
|
std::string get_triangle_as_string(int i) const;
|
||||||
|
|
||||||
// Before deserialization, reserve space for n_triangles.
|
// Before deserialization, reserve space for n_triangles.
|
||||||
void reserve(int n_triangles) { m_data.first.reserve(n_triangles); }
|
void reserve(int n_triangles) { m_data.triangles_to_split.reserve(n_triangles); }
|
||||||
// Deserialize triangles one by one, with strictly increasing triangle_id.
|
// Deserialize triangles one by one, with strictly increasing triangle_id.
|
||||||
void set_triangle_from_string(int triangle_id, const std::string& str);
|
void set_triangle_from_string(int triangle_id, const std::string& str);
|
||||||
// After deserializing the last triangle, shrink data to fit.
|
// After deserializing the last triangle, shrink data to fit.
|
||||||
void shrink_to_fit() { m_data.first.shrink_to_fit(); m_data.second.shrink_to_fit(); }
|
void shrink_to_fit() { m_data.triangles_to_split.shrink_to_fit(); m_data.bitstream.shrink_to_fit(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Constructors to be only called by derived classes.
|
// Constructors to be only called by derived classes.
|
||||||
@ -718,9 +695,9 @@ private:
|
|||||||
// by an existing ID copied from elsewhere.
|
// by an existing ID copied from elsewhere.
|
||||||
explicit FacetsAnnotation(int) : ObjectWithTimestamp(-1) {}
|
explicit FacetsAnnotation(int) : ObjectWithTimestamp(-1) {}
|
||||||
// Copy constructor copies the ID.
|
// Copy constructor copies the ID.
|
||||||
explicit FacetsAnnotation(const FacetsAnnotation &rhs) = default;
|
FacetsAnnotation(const FacetsAnnotation &rhs) = default;
|
||||||
// Move constructor copies the ID.
|
// Move constructor copies the ID.
|
||||||
explicit FacetsAnnotation(FacetsAnnotation &&rhs) = default;
|
FacetsAnnotation(FacetsAnnotation &&rhs) = default;
|
||||||
|
|
||||||
// called by ModelVolume::assign_copy()
|
// called by ModelVolume::assign_copy()
|
||||||
FacetsAnnotation& operator=(const FacetsAnnotation &rhs) = default;
|
FacetsAnnotation& operator=(const FacetsAnnotation &rhs) = default;
|
||||||
@ -729,12 +706,9 @@ private:
|
|||||||
friend class cereal::access;
|
friend class cereal::access;
|
||||||
friend class UndoRedo::StackImpl;
|
friend class UndoRedo::StackImpl;
|
||||||
|
|
||||||
template<class Archive> void serialize(Archive &ar)
|
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ObjectWithTimestamp>(this), m_data); }
|
||||||
{
|
|
||||||
ar(cereal::base_class<ObjectWithTimestamp>(this), m_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> m_data;
|
TriangleSelector::TriangleSplittingData m_data;
|
||||||
|
|
||||||
// To access set_new_unique_id() when copy / pasting a ModelVolume.
|
// To access set_new_unique_id() when copy / pasting a ModelVolume.
|
||||||
friend class ModelVolume;
|
friend class ModelVolume;
|
||||||
@ -936,6 +910,9 @@ public:
|
|||||||
bool is_seam_painted() const { return !this->seam_facets.empty(); }
|
bool is_seam_painted() const { return !this->seam_facets.empty(); }
|
||||||
bool is_mm_painted() const { return !this->mm_segmentation_facets.empty(); }
|
bool is_mm_painted() const { return !this->mm_segmentation_facets.empty(); }
|
||||||
|
|
||||||
|
// Returns 0-based indices of extruders painted by multi-material painting gizmo.
|
||||||
|
std::vector<size_t> get_extruders_from_multi_material_painting() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Print;
|
friend class Print;
|
||||||
friend class SLAPrint;
|
friend class SLAPrint;
|
||||||
|
@ -894,7 +894,7 @@ static inline std::vector<std::vector<ExPolygons>> mm_segmentation_top_and_botto
|
|||||||
if (mv->is_model_part()) {
|
if (mv->is_model_part()) {
|
||||||
const Transform3d volume_trafo = object_trafo * mv->get_matrix();
|
const Transform3d volume_trafo = object_trafo * mv->get_matrix();
|
||||||
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) {
|
for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) {
|
||||||
const indexed_triangle_set painted = mv->mm_segmentation_facets.get_facets_strict(*mv, EnforcerBlockerType(extruder_idx));
|
const indexed_triangle_set painted = mv->mm_segmentation_facets.get_facets_strict(*mv, TriangleStateType(extruder_idx));
|
||||||
#ifdef MM_SEGMENTATION_DEBUG_TOP_BOTTOM
|
#ifdef MM_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||||
{
|
{
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
@ -1298,7 +1298,7 @@ std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(con
|
|||||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_extruders + 1), [&mv, &print_object, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
|
||||||
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
|
||||||
throw_on_cancel_callback();
|
throw_on_cancel_callback();
|
||||||
const indexed_triangle_set custom_facets = mv->mm_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
|
const indexed_triangle_set custom_facets = mv->mm_segmentation_facets.get_facets(*mv, TriangleStateType(extruder_idx));
|
||||||
if (!mv->is_model_part() || custom_facets.indices.empty())
|
if (!mv->is_model_part() || custom_facets.indices.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -490,7 +490,9 @@ static std::vector<std::string> s_Preset_filament_options {
|
|||||||
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", "filament_retract_length_toolchange", "filament_retract_restart_extra_toolchange", "filament_travel_ramping_lift",
|
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe", "filament_retract_length_toolchange", "filament_retract_restart_extra_toolchange", "filament_travel_ramping_lift",
|
||||||
"filament_travel_slope", "filament_travel_max_lift", "filament_travel_lift_before_obstacle",
|
"filament_travel_slope", "filament_travel_max_lift", "filament_travel_lift_before_obstacle",
|
||||||
// Profile compatibility
|
// Profile compatibility
|
||||||
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
|
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||||
|
// Shrinkage compensation
|
||||||
|
"filament_shrinkage_compensation_xy", "filament_shrinkage_compensation_z",
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<std::string> s_Preset_machine_limits_options {
|
static std::vector<std::string> s_Preset_machine_limits_options {
|
||||||
|
@ -209,7 +209,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
// Spiral Vase forces different kind of slicing than the normal model:
|
// Spiral Vase forces different kind of slicing than the normal model:
|
||||||
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
|
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
|
||||||
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
|
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
|
||||||
|| opt_key == "spiral_vase") {
|
|| opt_key == "spiral_vase"
|
||||||
|
|| opt_key == "filament_shrinkage_compensation_xy"
|
||||||
|
|| opt_key == "filament_shrinkage_compensation_z") {
|
||||||
osteps.emplace_back(posSlice);
|
osteps.emplace_back(posSlice);
|
||||||
} else if (
|
} else if (
|
||||||
opt_key == "complete_objects"
|
opt_key == "complete_objects"
|
||||||
@ -525,6 +527,9 @@ std::string Print::validate(std::vector<std::string>* warnings) const
|
|||||||
goto DONE;
|
goto DONE;
|
||||||
}
|
}
|
||||||
DONE:;
|
DONE:;
|
||||||
|
|
||||||
|
if (!this->has_same_shrinkage_compensations())
|
||||||
|
warnings->emplace_back("_FILAMENT_SHRINKAGE_DIFFER");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_objects.empty())
|
if (m_objects.empty())
|
||||||
@ -584,12 +589,19 @@ std::string Print::validate(std::vector<std::string>* warnings) const
|
|||||||
//FIXME It is quite expensive to generate object layers just to get the print height!
|
//FIXME It is quite expensive to generate object layers just to get the print height!
|
||||||
if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx));
|
if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx));
|
||||||
! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) {
|
! layers.empty() && layers.back() > this->config().max_print_height + EPSILON) {
|
||||||
return
|
|
||||||
// Test whether the last slicing plane is below or above the print volume.
|
const double shrinkage_compensation_z = this->shrinkage_compensation().z();
|
||||||
0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().max_print_height + EPSILON ?
|
if (shrinkage_compensation_z != 1. && layers.back() > (this->config().max_print_height / shrinkage_compensation_z + EPSILON)) {
|
||||||
format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) :
|
// The object exceeds the maximum build volume height because of shrinkage compensation.
|
||||||
format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
|
return format(_u8L("While the object %1% itself fits the build volume, it exceeds the maximum build volume height because of material shrinkage compensation."), print_object.model_object()->name);
|
||||||
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry.");
|
} else if (0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().max_print_height + EPSILON) {
|
||||||
|
// The last slicing plane is below the print volume.
|
||||||
|
return format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name);
|
||||||
|
} else {
|
||||||
|
// The last slicing plane is above the print volume.
|
||||||
|
return format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
|
||||||
|
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,7 +772,7 @@ std::string Print::validate(std::vector<std::string>* warnings) const
|
|||||||
if (! object->has_support() && warnings) {
|
if (! object->has_support() && warnings) {
|
||||||
for (const ModelVolume* mv : object->model_object()->volumes) {
|
for (const ModelVolume* mv : object->model_object()->volumes) {
|
||||||
bool has_enforcers = mv->is_support_enforcer() ||
|
bool has_enforcers = mv->is_support_enforcer() ||
|
||||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, TriangleStateType::ENFORCER));
|
||||||
if (has_enforcers) {
|
if (has_enforcers) {
|
||||||
warnings->emplace_back("_SUPPORTS_OFF");
|
warnings->emplace_back("_SUPPORTS_OFF");
|
||||||
break;
|
break;
|
||||||
@ -1618,6 +1630,40 @@ std::string Print::output_filename(const std::string &filename_base) const
|
|||||||
return this->PrintBase::output_filename(output_filename_format, ".gcode", filename_base, &config);
|
return this->PrintBase::output_filename(output_filename_format, ".gcode", filename_base, &config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns if all used filaments have same shrinkage compensations.
|
||||||
|
bool Print::has_same_shrinkage_compensations() const {
|
||||||
|
const std::vector<unsigned int> extruders = this->extruders();
|
||||||
|
if (extruders.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const double filament_shrinkage_compensation_xy = m_config.filament_shrinkage_compensation_xy.get_at(extruders.front());
|
||||||
|
const double filament_shrinkage_compensation_z = m_config.filament_shrinkage_compensation_z.get_at(extruders.front());
|
||||||
|
|
||||||
|
for (unsigned int extruder : extruders) {
|
||||||
|
if (filament_shrinkage_compensation_xy != m_config.filament_shrinkage_compensation_xy.get_at(extruder) ||
|
||||||
|
filament_shrinkage_compensation_z != m_config.filament_shrinkage_compensation_z.get_at(extruder)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns scaling for each axis representing shrinkage compensations in each axis.
|
||||||
|
Vec3d Print::shrinkage_compensation() const
|
||||||
|
{
|
||||||
|
if (!this->has_same_shrinkage_compensations())
|
||||||
|
return Vec3d::Ones();
|
||||||
|
|
||||||
|
const unsigned int first_extruder = this->extruders().front();
|
||||||
|
const double xy_compensation_percent = std::clamp(m_config.filament_shrinkage_compensation_xy.get_at(first_extruder), -99., 99.);
|
||||||
|
const double z_compensation_percent = std::clamp(m_config.filament_shrinkage_compensation_z.get_at(first_extruder), -99., 99.);
|
||||||
|
const double xy_compensation = 100. / (100. - xy_compensation_percent);
|
||||||
|
const double z_compensation = 100. / (100. - z_compensation_percent);
|
||||||
|
|
||||||
|
return { xy_compensation, xy_compensation, z_compensation };
|
||||||
|
}
|
||||||
|
|
||||||
const std::string PrintStatistics::FilamentUsedG = "filament used [g]";
|
const std::string PrintStatistics::FilamentUsedG = "filament used [g]";
|
||||||
const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] =";
|
const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] =";
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ public:
|
|||||||
// The slicing parameters are dependent on various configuration values
|
// The slicing parameters are dependent on various configuration values
|
||||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||||
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
|
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
|
||||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
|
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation);
|
||||||
|
|
||||||
size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); }
|
size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); }
|
||||||
const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); }
|
const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); }
|
||||||
@ -353,7 +353,7 @@ public:
|
|||||||
std::vector<Polygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
std::vector<Polygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
|
||||||
|
|
||||||
// Helpers to project custom facets on slices
|
// Helpers to project custom facets on slices
|
||||||
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<Polygons>& expolys) const;
|
void project_and_append_custom_facets(bool seam, TriangleStateType type, std::vector<Polygons>& expolys) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// to be called from Print only.
|
// to be called from Print only.
|
||||||
@ -665,6 +665,12 @@ public:
|
|||||||
const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; }
|
const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; }
|
||||||
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
||||||
|
|
||||||
|
// Returns if all used filaments have same shrinkage compensations.
|
||||||
|
bool has_same_shrinkage_compensations() const;
|
||||||
|
|
||||||
|
// Returns scaling for each axis representing shrinkage compensations in each axis.
|
||||||
|
Vec3d shrinkage_compensation() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Invalidates the step, and its depending steps in Print.
|
// Invalidates the step, and its depending steps in Print.
|
||||||
bool invalidate_step(PrintStep step);
|
bool invalidate_step(PrintStep step);
|
||||||
|
@ -134,13 +134,14 @@ struct PrintObjectTrafoAndInstances
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||||
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
|
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object, const Vec3d &shrinkage_compensation)
|
||||||
{
|
{
|
||||||
std::set<PrintObjectTrafoAndInstances> trafos;
|
std::set<PrintObjectTrafoAndInstances> trafos;
|
||||||
PrintObjectTrafoAndInstances trafo;
|
PrintObjectTrafoAndInstances trafo;
|
||||||
for (ModelInstance *model_instance : model_object.instances)
|
for (ModelInstance *model_instance : model_object.instances)
|
||||||
if (model_instance->is_printable()) {
|
if (model_instance->is_printable()) {
|
||||||
trafo.trafo = model_instance->get_matrix();
|
Geometry::Transformation model_instance_transformation = model_instance->get_transformation();
|
||||||
|
trafo.trafo = model_instance_transformation.get_matrix_with_applied_shrinkage_compensation(shrinkage_compensation);
|
||||||
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
|
||||||
// Reset the XY axes of the transformation.
|
// Reset the XY axes of the transformation.
|
||||||
trafo.trafo.data()[12] = 0;
|
trafo.trafo.data()[12] = 0;
|
||||||
@ -1281,7 +1282,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
// Walk over all new model objects and check, whether there are matching PrintObjects.
|
// Walk over all new model objects and check, whether there are matching PrintObjects.
|
||||||
for (ModelObject *model_object : m_model.objects) {
|
for (ModelObject *model_object : m_model.objects) {
|
||||||
ModelObjectStatus &model_object_status = const_cast<ModelObjectStatus&>(model_object_status_db.reuse(*model_object));
|
ModelObjectStatus &model_object_status = const_cast<ModelObjectStatus&>(model_object_status_db.reuse(*model_object));
|
||||||
model_object_status.print_instances = print_objects_from_model_object(*model_object);
|
model_object_status.print_instances = print_objects_from_model_object(*model_object, this->shrinkage_compensation());
|
||||||
std::vector<const PrintObjectStatus*> old;
|
std::vector<const PrintObjectStatus*> old;
|
||||||
old.reserve(print_object_status_db.count(*model_object));
|
old.reserve(print_object_status_db.count(*model_object));
|
||||||
for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(*model_object))
|
for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(*model_object))
|
||||||
@ -1375,9 +1376,20 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
if (const auto &volumes = print_object.model_object()->volumes;
|
if (const auto &volumes = print_object.model_object()->volumes;
|
||||||
num_extruders > 1 &&
|
num_extruders > 1 &&
|
||||||
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mm_segmentation_facets.empty(); }) != volumes.end()) {
|
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mm_segmentation_facets.empty(); }) != volumes.end()) {
|
||||||
//FIXME be more specific! Don't enumerate extruders that are not used for painting!
|
|
||||||
painting_extruders.assign(num_extruders, 0);
|
std::array<bool, static_cast<size_t>(TriangleStateType::Count)> used_facet_states{};
|
||||||
std::iota(painting_extruders.begin(), painting_extruders.end(), 1);
|
for (const ModelVolume *volume : volumes) {
|
||||||
|
const std::vector<bool> &volume_used_facet_states = volume->mm_segmentation_facets.get_data().used_states;
|
||||||
|
|
||||||
|
assert(volume_used_facet_states.size() == used_facet_states.size());
|
||||||
|
for (size_t state_idx = 0; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx)
|
||||||
|
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t state_idx = static_cast<size_t>(TriangleStateType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
|
||||||
|
if (used_facet_states[state_idx])
|
||||||
|
painting_extruders.emplace_back(state_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
|
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
|
||||||
// Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
|
// Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
|
||||||
|
@ -1332,6 +1332,28 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->set_default_value(new ConfigOptionString(L("(Unknown)")));
|
def->set_default_value(new ConfigOptionString(L("(Unknown)")));
|
||||||
def->cli = ConfigOptionDef::nocli;
|
def->cli = ConfigOptionDef::nocli;
|
||||||
|
|
||||||
|
def = this->add("filament_shrinkage_compensation_xy", coPercents);
|
||||||
|
def->label = L("Shrinkage XY");
|
||||||
|
def->tooltip = L("Enter your filament shrinkage percentages for the X and Y axes here to apply scaling of the object to "
|
||||||
|
"compensate for shrinkage in the X and Y axes. For example, if you measured 99mm instead of 100mm, "
|
||||||
|
"then you should put here 1%.");
|
||||||
|
def->sidetext = L("%");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->min = -10.;
|
||||||
|
def->max = 10.;
|
||||||
|
def->set_default_value(new ConfigOptionPercents { 0 });
|
||||||
|
|
||||||
|
def = this->add("filament_shrinkage_compensation_z", coPercents);
|
||||||
|
def->label = L("Shrinkage Z");
|
||||||
|
def->tooltip = L("Enter your filament shrinkage percentages for the Z axis here to apply scaling of the object to "
|
||||||
|
"compensate for shrinkage in the Z axis. For example, if you measured 99mm instead of 100mm, "
|
||||||
|
"then you should put here 1%.");
|
||||||
|
def->sidetext = L("%");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->min = -10.;
|
||||||
|
def->max = 10.;
|
||||||
|
def->set_default_value(new ConfigOptionPercents { 0. });
|
||||||
|
|
||||||
def = this->add("fill_angle", coFloat);
|
def = this->add("fill_angle", coFloat);
|
||||||
def->label = L("Fill angle");
|
def->label = L("Fill angle");
|
||||||
def->category = L("Infill");
|
def->category = L("Infill");
|
||||||
|
@ -745,6 +745,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionFloats, filament_multitool_ramming_flow))
|
((ConfigOptionFloats, filament_multitool_ramming_flow))
|
||||||
((ConfigOptionFloats, filament_stamping_loading_speed))
|
((ConfigOptionFloats, filament_stamping_loading_speed))
|
||||||
((ConfigOptionFloats, filament_stamping_distance))
|
((ConfigOptionFloats, filament_stamping_distance))
|
||||||
|
((ConfigOptionPercents, filament_shrinkage_compensation_xy))
|
||||||
|
((ConfigOptionPercents, filament_shrinkage_compensation_z))
|
||||||
((ConfigOptionBool, gcode_comments))
|
((ConfigOptionBool, gcode_comments))
|
||||||
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
((ConfigOptionEnum<GCodeFlavor>, gcode_flavor))
|
||||||
((ConfigOptionEnum<LabelObjectsStyle>, gcode_label_objects))
|
((ConfigOptionEnum<LabelObjectsStyle>, gcode_label_objects))
|
||||||
|
@ -2620,15 +2620,14 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintObject::update_slicing_parameters()
|
void PrintObject::update_slicing_parameters() {
|
||||||
{
|
if (!m_slicing_params.valid) {
|
||||||
if (!m_slicing_params.valid)
|
m_slicing_params = SlicingParameters::create_from_config(this->print()->config(), m_config, this->model_object()->max_z(),
|
||||||
m_slicing_params = SlicingParameters::create_from_config(
|
this->object_extruders(), this->print()->shrinkage_compensation());
|
||||||
this->print()->config(), m_config, this->model_object()->max_z(), this->object_extruders());
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation) {
|
||||||
{
|
|
||||||
PrintConfig print_config;
|
PrintConfig print_config;
|
||||||
PrintObjectConfig object_config;
|
PrintObjectConfig object_config;
|
||||||
PrintRegionConfig default_region_config;
|
PrintRegionConfig default_region_config;
|
||||||
@ -2661,7 +2660,8 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
|
|||||||
|
|
||||||
if (object_max_z <= 0.f)
|
if (object_max_z <= 0.f)
|
||||||
object_max_z = (float)model_object.raw_bounding_box().size().z();
|
object_max_z = (float)model_object.raw_bounding_box().size().z();
|
||||||
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
|
|
||||||
|
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders, object_shrinkage_compensation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||||
@ -2682,7 +2682,6 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
|||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
// use the constructor because the assignement is crashing on ASAN OsX
|
// use the constructor because the assignement is crashing on ASAN OsX
|
||||||
layer_height_profile = model_object.layer_height_profile.get();
|
layer_height_profile = model_object.layer_height_profile.get();
|
||||||
// layer_height_profile = model_object.layer_height_profile;
|
|
||||||
// The layer height returned is sampled with high density for the UI layer height painting
|
// The layer height returned is sampled with high density for the UI layer height painting
|
||||||
// and smoothing tool to work.
|
// and smoothing tool to work.
|
||||||
updated = true;
|
updated = true;
|
||||||
@ -2693,8 +2692,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
|||||||
// Must not be of even length.
|
// Must not be of even length.
|
||||||
((layer_height_profile.size() & 1) != 0 ||
|
((layer_height_profile.size() & 1) != 0 ||
|
||||||
// Last entry must be at the top of the object.
|
// Last entry must be at the top of the object.
|
||||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max + slicing_parameters.object_print_z_min) > 1e-3))
|
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_uncompensated_max + slicing_parameters.object_print_z_min) > 1e-3)) {
|
||||||
layer_height_profile.clear();
|
layer_height_profile.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
||||||
@ -3150,7 +3150,7 @@ static void project_triangles_to_slabs(SpanOfConstPtrs<Layer> layers, const inde
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PrintObject::project_and_append_custom_facets(
|
void PrintObject::project_and_append_custom_facets(
|
||||||
bool seam, EnforcerBlockerType type, std::vector<Polygons>& out) const
|
bool seam, TriangleStateType type, std::vector<Polygons>& out) const
|
||||||
{
|
{
|
||||||
for (const ModelVolume* mv : this->model_object()->volumes)
|
for (const ModelVolume* mv : this->model_object()->volumes)
|
||||||
if (mv->is_model_part()) {
|
if (mv->is_model_part()) {
|
||||||
|
@ -63,10 +63,11 @@ coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_c
|
|||||||
}
|
}
|
||||||
|
|
||||||
SlicingParameters SlicingParameters::create_from_config(
|
SlicingParameters SlicingParameters::create_from_config(
|
||||||
const PrintConfig &print_config,
|
const PrintConfig &print_config,
|
||||||
const PrintObjectConfig &object_config,
|
const PrintObjectConfig &object_config,
|
||||||
coordf_t object_height,
|
coordf_t object_height,
|
||||||
const std::vector<unsigned int> &object_extruders)
|
const std::vector<unsigned int> &object_extruders,
|
||||||
|
const Vec3d &object_shrinkage_compensation)
|
||||||
{
|
{
|
||||||
assert(! print_config.first_layer_height.percent);
|
assert(! print_config.first_layer_height.percent);
|
||||||
coordf_t first_layer_height = (print_config.first_layer_height.value <= 0) ?
|
coordf_t first_layer_height = (print_config.first_layer_height.value <= 0) ?
|
||||||
@ -85,7 +86,9 @@ SlicingParameters SlicingParameters::create_from_config(
|
|||||||
params.first_print_layer_height = first_layer_height;
|
params.first_print_layer_height = first_layer_height;
|
||||||
params.first_object_layer_height = first_layer_height;
|
params.first_object_layer_height = first_layer_height;
|
||||||
params.object_print_z_min = 0.;
|
params.object_print_z_min = 0.;
|
||||||
params.object_print_z_max = object_height;
|
params.object_print_z_max = object_height * object_shrinkage_compensation.z();
|
||||||
|
params.object_print_z_uncompensated_max = object_height;
|
||||||
|
params.object_shrinkage_compensation_z = object_shrinkage_compensation.z();
|
||||||
params.base_raft_layers = object_config.raft_layers.value;
|
params.base_raft_layers = object_config.raft_layers.value;
|
||||||
params.soluble_interface = soluble_interface;
|
params.soluble_interface = soluble_interface;
|
||||||
|
|
||||||
@ -152,6 +155,7 @@ SlicingParameters SlicingParameters::create_from_config(
|
|||||||
coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object;
|
coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object;
|
||||||
params.object_print_z_min = print_z;
|
params.object_print_z_min = print_z;
|
||||||
params.object_print_z_max += print_z;
|
params.object_print_z_max += print_z;
|
||||||
|
params.object_print_z_uncompensated_max += print_z;
|
||||||
}
|
}
|
||||||
|
|
||||||
params.valid = true;
|
params.valid = true;
|
||||||
@ -224,10 +228,10 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
|
|||||||
lh_append(hi, height);
|
lh_append(hi, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coordf_t z = last_z(); z < slicing_params.object_print_z_height()) {
|
if (coordf_t z = last_z(); z < slicing_params.object_print_z_uncompensated_height()) {
|
||||||
// Insert a step of normal layer height up to the object top.
|
// Insert a step of normal layer height up to the object top.
|
||||||
lh_append(z, slicing_params.layer_height);
|
lh_append(z, slicing_params.layer_height);
|
||||||
lh_append(slicing_params.object_print_z_height(), slicing_params.layer_height);
|
lh_append(slicing_params.object_print_z_uncompensated_height(), slicing_params.layer_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
return layer_height_profile;
|
return layer_height_profile;
|
||||||
@ -418,12 +422,12 @@ void adjust_layer_height_profile(
|
|||||||
std::pair<coordf_t, coordf_t> z_span_variable =
|
std::pair<coordf_t, coordf_t> z_span_variable =
|
||||||
std::pair<coordf_t, coordf_t>(
|
std::pair<coordf_t, coordf_t>(
|
||||||
slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
|
slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
|
||||||
slicing_params.object_print_z_height());
|
slicing_params.object_print_z_uncompensated_height());
|
||||||
if (z < z_span_variable.first || z > z_span_variable.second)
|
if (z < z_span_variable.first || z > z_span_variable.second)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(layer_height_profile.size() >= 2);
|
assert(layer_height_profile.size() >= 2);
|
||||||
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON);
|
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_uncompensated_height()) < EPSILON);
|
||||||
|
|
||||||
// 1) Get the current layer thickness at z.
|
// 1) Get the current layer thickness at z.
|
||||||
coordf_t current_layer_height = slicing_params.layer_height;
|
coordf_t current_layer_height = slicing_params.layer_height;
|
||||||
@ -584,7 +588,7 @@ void adjust_layer_height_profile(
|
|||||||
assert(layer_height_profile.size() > 2);
|
assert(layer_height_profile.size() > 2);
|
||||||
assert(layer_height_profile.size() % 2 == 0);
|
assert(layer_height_profile.size() % 2 == 0);
|
||||||
assert(layer_height_profile[0] == 0.);
|
assert(layer_height_profile[0] == 0.);
|
||||||
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON);
|
assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_uncompensated_height()) < EPSILON);
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
for (size_t i = 2; i < layer_height_profile.size(); i += 2)
|
for (size_t i = 2; i < layer_height_profile.size(); i += 2)
|
||||||
assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
|
assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
|
||||||
@ -613,6 +617,7 @@ std::vector<coordf_t> generate_object_layers(
|
|||||||
out.push_back(print_z);
|
out.push_back(print_z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const coordf_t shrinkage_compensation_z = slicing_params.object_shrinkage_compensation_z;
|
||||||
size_t idx_layer_height_profile = 0;
|
size_t idx_layer_height_profile = 0;
|
||||||
// loop until we have at least one layer and the max slice_z reaches the object height
|
// loop until we have at least one layer and the max slice_z reaches the object height
|
||||||
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
|
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
|
||||||
@ -621,17 +626,18 @@ std::vector<coordf_t> generate_object_layers(
|
|||||||
if (idx_layer_height_profile < layer_height_profile.size()) {
|
if (idx_layer_height_profile < layer_height_profile.size()) {
|
||||||
size_t next = idx_layer_height_profile + 2;
|
size_t next = idx_layer_height_profile + 2;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
|
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next] * shrinkage_compensation_z)
|
||||||
break;
|
break;
|
||||||
idx_layer_height_profile = next;
|
idx_layer_height_profile = next;
|
||||||
next += 2;
|
next += 2;
|
||||||
}
|
}
|
||||||
coordf_t z1 = layer_height_profile[idx_layer_height_profile];
|
|
||||||
coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
|
const coordf_t z1 = layer_height_profile[idx_layer_height_profile] * shrinkage_compensation_z;
|
||||||
|
const coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
|
||||||
height = h1;
|
height = h1;
|
||||||
if (next < layer_height_profile.size()) {
|
if (next < layer_height_profile.size()) {
|
||||||
coordf_t z2 = layer_height_profile[next];
|
const coordf_t z2 = layer_height_profile[next] * shrinkage_compensation_z;
|
||||||
coordf_t h2 = layer_height_profile[next + 1];
|
const coordf_t h2 = layer_height_profile[next + 1];
|
||||||
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
|
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
|
||||||
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
|
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,11 @@ struct SlicingParameters
|
|||||||
SlicingParameters() = default;
|
SlicingParameters() = default;
|
||||||
|
|
||||||
static SlicingParameters create_from_config(
|
static SlicingParameters create_from_config(
|
||||||
const PrintConfig &print_config,
|
const PrintConfig &print_config,
|
||||||
const PrintObjectConfig &object_config,
|
const PrintObjectConfig &object_config,
|
||||||
coordf_t object_height,
|
coordf_t object_height,
|
||||||
const std::vector<unsigned int> &object_extruders);
|
const std::vector<unsigned int> &object_extruders,
|
||||||
|
const Vec3d &object_shrinkage_compensation);
|
||||||
|
|
||||||
// Has any raft layers?
|
// Has any raft layers?
|
||||||
bool has_raft() const { return raft_layers() > 0; }
|
bool has_raft() const { return raft_layers() > 0; }
|
||||||
@ -46,8 +47,13 @@ struct SlicingParameters
|
|||||||
bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; }
|
bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; }
|
||||||
|
|
||||||
// Height of the object to be printed. This value does not contain the raft height.
|
// Height of the object to be printed. This value does not contain the raft height.
|
||||||
|
// This value is scaled by shrinkage compensation in the Z-axis.
|
||||||
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
|
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
|
||||||
|
|
||||||
|
// Height of the object to be printed. This value does not contain the raft height.
|
||||||
|
// This value isn't scaled by shrinkage compensation in the Z-axis.
|
||||||
|
coordf_t object_print_z_uncompensated_height() const { return object_print_z_uncompensated_max - object_print_z_min; }
|
||||||
|
|
||||||
bool valid { false };
|
bool valid { false };
|
||||||
|
|
||||||
// Number of raft layers.
|
// Number of raft layers.
|
||||||
@ -99,7 +105,13 @@ struct SlicingParameters
|
|||||||
coordf_t raft_contact_top_z { 0 };
|
coordf_t raft_contact_top_z { 0 };
|
||||||
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
|
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
|
||||||
coordf_t object_print_z_min { 0 };
|
coordf_t object_print_z_min { 0 };
|
||||||
|
// This value of maximum print Z is scaled by shrinkage compensation in the Z-axis.
|
||||||
coordf_t object_print_z_max { 0 };
|
coordf_t object_print_z_max { 0 };
|
||||||
|
|
||||||
|
// This value of maximum print Z isn't scaled by shrinkage compensation.
|
||||||
|
coordf_t object_print_z_uncompensated_max { 0 };
|
||||||
|
// Scaling factor for compensating shrinkage in Z-axis.
|
||||||
|
coordf_t object_shrinkage_compensation_z { 0 };
|
||||||
};
|
};
|
||||||
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
|
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
|
||||||
|
|
||||||
|
@ -1106,8 +1106,8 @@ struct SupportAnnotations
|
|||||||
buildplate_covered(buildplate_covered)
|
buildplate_covered(buildplate_covered)
|
||||||
{
|
{
|
||||||
// Append custom supports.
|
// Append custom supports.
|
||||||
object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers);
|
object.project_and_append_custom_facets(false, TriangleStateType::ENFORCER, enforcers_layers);
|
||||||
object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers);
|
object.project_and_append_custom_facets(false, TriangleStateType::BLOCKER, blockers_layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Polygons> enforcers_layers;
|
std::vector<Polygons> enforcers_layers;
|
||||||
|
@ -207,8 +207,8 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
|||||||
const int support_enforce_layers = config.support_material_enforce_layers.value;
|
const int support_enforce_layers = config.support_material_enforce_layers.value;
|
||||||
std::vector<Polygons> enforcers_layers{ print_object.slice_support_enforcers() };
|
std::vector<Polygons> enforcers_layers{ print_object.slice_support_enforcers() };
|
||||||
std::vector<Polygons> blockers_layers{ print_object.slice_support_blockers() };
|
std::vector<Polygons> blockers_layers{ print_object.slice_support_blockers() };
|
||||||
print_object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers);
|
print_object.project_and_append_custom_facets(false, TriangleStateType::ENFORCER, enforcers_layers);
|
||||||
print_object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers);
|
print_object.project_and_append_custom_facets(false, TriangleStateType::BLOCKER, blockers_layers);
|
||||||
const int support_threshold = config.support_material_threshold.value;
|
const int support_threshold = config.support_material_threshold.value;
|
||||||
const bool support_threshold_auto = support_threshold == 0;
|
const bool support_threshold_auto = support_threshold == 0;
|
||||||
// +1 makes the threshold inclusive
|
// +1 makes the threshold inclusive
|
||||||
|
@ -234,7 +234,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
|
|||||||
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg)
|
void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, TriangleStateType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg)
|
||||||
{
|
{
|
||||||
assert(facet_start < m_orig_size_indices);
|
assert(facet_start < m_orig_size_indices);
|
||||||
|
|
||||||
@ -455,7 +455,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
assert(!m_triangles[start_facet_idx].is_split());
|
assert(!m_triangles[start_facet_idx].is_split());
|
||||||
EnforcerBlockerType start_facet_state = m_triangles[start_facet_idx].get_state();
|
TriangleStateType start_facet_state = m_triangles[start_facet_idx].get_state();
|
||||||
this->seed_fill_unselect_all_triangles();
|
this->seed_fill_unselect_all_triangles();
|
||||||
|
|
||||||
if (!propagate) {
|
if (!propagate) {
|
||||||
@ -511,8 +511,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
|||||||
// This is done by an actual recursive call. Returns false if the triangle is
|
// This is done by an actual recursive call. Returns false if the triangle is
|
||||||
// outside the cursor.
|
// outside the cursor.
|
||||||
// Called by select_patch() and by itself.
|
// Called by select_patch() and by itself.
|
||||||
bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting)
|
bool TriangleSelector::select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting) {
|
||||||
{
|
|
||||||
assert(facet_idx < int(m_triangles.size()));
|
assert(facet_idx < int(m_triangles.size()));
|
||||||
|
|
||||||
if (! m_triangles[facet_idx].valid())
|
if (! m_triangles[facet_idx].valid())
|
||||||
@ -861,8 +860,7 @@ Vec3i TriangleSelector::child_neighbors_propagated(const Triangle &tr, const Vec
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting)
|
bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting) {
|
||||||
{
|
|
||||||
assert(facet_idx < int(m_triangles.size()));
|
assert(facet_idx < int(m_triangles.size()));
|
||||||
|
|
||||||
Triangle* tr = &m_triangles[facet_idx];
|
Triangle* tr = &m_triangles[facet_idx];
|
||||||
@ -914,8 +912,7 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i &nei
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::set_facet(int facet_idx, EnforcerBlockerType state)
|
void TriangleSelector::set_facet(int facet_idx, TriangleStateType state) {
|
||||||
{
|
|
||||||
assert(facet_idx < m_orig_size_indices);
|
assert(facet_idx < m_orig_size_indices);
|
||||||
undivide_triangle(facet_idx);
|
undivide_triangle(facet_idx);
|
||||||
assert(! m_triangles[facet_idx].is_split());
|
assert(! m_triangles[facet_idx].is_split());
|
||||||
@ -934,7 +931,7 @@ void TriangleSelector::split_triangle(int facet_idx, const Vec3i &neighbors)
|
|||||||
Triangle* tr = &m_triangles[facet_idx];
|
Triangle* tr = &m_triangles[facet_idx];
|
||||||
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
assert(this->verify_triangle_neighbors(*tr, neighbors));
|
||||||
|
|
||||||
EnforcerBlockerType old_type = tr->get_state();
|
TriangleStateType old_type = tr->get_state();
|
||||||
|
|
||||||
// If we got here, we are about to actually split the triangle.
|
// If we got here, we are about to actually split the triangle.
|
||||||
const double limit_squared = m_edge_limit_sqr;
|
const double limit_squared = m_edge_limit_sqr;
|
||||||
@ -1117,7 +1114,7 @@ void TriangleSelector::remove_useless_children(int facet_idx)
|
|||||||
|
|
||||||
|
|
||||||
// Return if a child is not leaf or two children differ in type.
|
// Return if a child is not leaf or two children differ in type.
|
||||||
EnforcerBlockerType first_child_type = EnforcerBlockerType::NONE;
|
TriangleStateType first_child_type = TriangleStateType::NONE;
|
||||||
for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) {
|
for (int child_idx=0; child_idx<=tr.number_of_split_sides(); ++child_idx) {
|
||||||
if (m_triangles[tr.children[child_idx]].is_split())
|
if (m_triangles[tr.children[child_idx]].is_split())
|
||||||
return;
|
return;
|
||||||
@ -1217,8 +1214,7 @@ void TriangleSelector::set_edge_limit(float edge_limit)
|
|||||||
m_edge_limit_sqr = std::pow(edge_limit, 2.f);
|
m_edge_limit_sqr = std::pow(edge_limit, 2.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state)
|
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const TriangleStateType state) {
|
||||||
{
|
|
||||||
for (int i : {a, b, c}) {
|
for (int i : {a, b, c}) {
|
||||||
assert(i >= 0 && i < int(m_vertices.size()));
|
assert(i >= 0 && i < int(m_vertices.size()));
|
||||||
++m_vertices[i].ref_cnt;
|
++m_vertices[i].ref_cnt;
|
||||||
@ -1251,8 +1247,7 @@ int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, co
|
|||||||
// Split a triangle based on Triangle::number_of_split_sides() and Triangle::special_side()
|
// Split a triangle based on Triangle::number_of_split_sides() and Triangle::special_side()
|
||||||
// by allocating child triangles and midpoint vertices.
|
// by allocating child triangles and midpoint vertices.
|
||||||
// Midpoint vertices are possibly reused by traversing children of neighbor triangles.
|
// Midpoint vertices are possibly reused by traversing children of neighbor triangles.
|
||||||
void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state)
|
void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state) {
|
||||||
{
|
|
||||||
// Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
|
// Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
|
||||||
{
|
{
|
||||||
size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1;
|
size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1;
|
||||||
@ -1318,16 +1313,14 @@ void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, Enfo
|
|||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TriangleSelector::has_facets(EnforcerBlockerType state) const
|
bool TriangleSelector::has_facets(TriangleStateType state) const {
|
||||||
{
|
|
||||||
for (const Triangle& tr : m_triangles)
|
for (const Triangle& tr : m_triangles)
|
||||||
if (tr.valid() && ! tr.is_split() && tr.get_state() == state)
|
if (tr.valid() && ! tr.is_split() && tr.get_state() == state)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TriangleSelector::num_facets(EnforcerBlockerType state) const
|
int TriangleSelector::num_facets(TriangleStateType state) const {
|
||||||
{
|
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
for (const Triangle& tr : m_triangles)
|
for (const Triangle& tr : m_triangles)
|
||||||
if (tr.valid() && ! tr.is_split() && tr.get_state() == state)
|
if (tr.valid() && ! tr.is_split() && tr.get_state() == state)
|
||||||
@ -1335,8 +1328,7 @@ int TriangleSelector::num_facets(EnforcerBlockerType state) const
|
|||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) const
|
indexed_triangle_set TriangleSelector::get_facets(TriangleStateType state) const {
|
||||||
{
|
|
||||||
indexed_triangle_set out;
|
indexed_triangle_set out;
|
||||||
std::vector<int> vertex_map(m_vertices.size(), -1);
|
std::vector<int> vertex_map(m_vertices.size(), -1);
|
||||||
for (const Triangle& tr : m_triangles) {
|
for (const Triangle& tr : m_triangles) {
|
||||||
@ -1356,8 +1348,7 @@ indexed_triangle_set TriangleSelector::get_facets(EnforcerBlockerType state) con
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType state) const
|
indexed_triangle_set TriangleSelector::get_facets_strict(TriangleStateType state) const {
|
||||||
{
|
|
||||||
indexed_triangle_set out;
|
indexed_triangle_set out;
|
||||||
|
|
||||||
size_t num_vertices = 0;
|
size_t num_vertices = 0;
|
||||||
@ -1385,7 +1376,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta
|
|||||||
void TriangleSelector::get_facets_strict_recursive(
|
void TriangleSelector::get_facets_strict_recursive(
|
||||||
const Triangle &tr,
|
const Triangle &tr,
|
||||||
const Vec3i &neighbors,
|
const Vec3i &neighbors,
|
||||||
EnforcerBlockerType state,
|
TriangleStateType state,
|
||||||
std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
std::vector<stl_triangle_vertex_indices> &out_triangles) const
|
||||||
{
|
{
|
||||||
if (tr.is_split()) {
|
if (tr.is_split()) {
|
||||||
@ -1526,12 +1517,11 @@ void TriangleSelector::get_seed_fill_contour_recursive(const int facet_idx, cons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector::serialize() const
|
TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
||||||
{
|
|
||||||
// Each original triangle of the mesh is assigned a number encoding its state
|
// Each original triangle of the mesh is assigned a number encoding its state
|
||||||
// or how it is split. Each triangle is encoded by 4 bits (xxyy) or 8 bits (zzzzxxyy):
|
// or how it is split. Each triangle is encoded by 4 bits (xxyy) or 8 bits (zzzzxxyy):
|
||||||
// leaf triangle: xx = EnforcerBlockerType (Only values 0, 1, and 2. Value 3 is used as an indicator for additional 4 bits.), yy = 0
|
// leaf triangle: xx = TriangleStateType (Only values 0, 1, and 2. Value 3 is used as an indicator for additional 4 bits.), yy = 0
|
||||||
// leaf triangle: xx = 0b11, yy = 0b00, zzzz = EnforcerBlockerType (subtracted by 3)
|
// leaf triangle: xx = 0b11, yy = 0b00, zzzz = TriangleStateType (subtracted by 3)
|
||||||
// non-leaf: xx = special side, yy = number of split sides
|
// non-leaf: xx = special side, yy = number of split sides
|
||||||
// These are bitwise appended and formed into one 64-bit integer.
|
// These are bitwise appended and formed into one 64-bit integer.
|
||||||
|
|
||||||
@ -1543,7 +1533,7 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
|
|||||||
// (std::function calls using a pointer, while this implementation calls directly).
|
// (std::function calls using a pointer, while this implementation calls directly).
|
||||||
struct Serializer {
|
struct Serializer {
|
||||||
const TriangleSelector* triangle_selector;
|
const TriangleSelector* triangle_selector;
|
||||||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> data;
|
TriangleSplittingData data;
|
||||||
|
|
||||||
void serialize(int facet_idx) {
|
void serialize(int facet_idx) {
|
||||||
const Triangle& tr = triangle_selector->m_triangles[facet_idx];
|
const Triangle& tr = triangle_selector->m_triangles[facet_idx];
|
||||||
@ -1552,8 +1542,8 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
|
|||||||
int split_sides = tr.number_of_split_sides();
|
int split_sides = tr.number_of_split_sides();
|
||||||
assert(split_sides >= 0 && split_sides <= 3);
|
assert(split_sides >= 0 && split_sides <= 3);
|
||||||
|
|
||||||
data.second.push_back(split_sides & 0b01);
|
data.bitstream.push_back(split_sides & 0b01);
|
||||||
data.second.push_back(split_sides & 0b10);
|
data.bitstream.push_back(split_sides & 0b10);
|
||||||
|
|
||||||
if (split_sides) {
|
if (split_sides) {
|
||||||
// If this triangle is split, save which side is split (in case
|
// If this triangle is split, save which side is split (in case
|
||||||
@ -1561,8 +1551,8 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
|
|||||||
// be ignored for 3-side split.
|
// be ignored for 3-side split.
|
||||||
assert(tr.is_split() && split_sides > 0);
|
assert(tr.is_split() && split_sides > 0);
|
||||||
assert(tr.special_side() >= 0 && tr.special_side() <= 3);
|
assert(tr.special_side() >= 0 && tr.special_side() <= 3);
|
||||||
data.second.push_back(tr.special_side() & 0b01);
|
data.bitstream.push_back(tr.special_side() & 0b01);
|
||||||
data.second.push_back(tr.special_side() & 0b10);
|
data.bitstream.push_back(tr.special_side() & 0b10);
|
||||||
// Now save all children.
|
// Now save all children.
|
||||||
// Serialized in reverse order for compatibility with PrusaSlicer 2.3.1.
|
// Serialized in reverse order for compatibility with PrusaSlicer 2.3.1.
|
||||||
for (int child_idx = split_sides; child_idx >= 0; -- child_idx)
|
for (int child_idx = split_sides; child_idx >= 0; -- child_idx)
|
||||||
@ -1570,48 +1560,50 @@ std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> TriangleSelector:
|
|||||||
} else {
|
} else {
|
||||||
// In case this is leaf, we better save information about its state.
|
// In case this is leaf, we better save information about its state.
|
||||||
int n = int(tr.get_state());
|
int n = int(tr.get_state());
|
||||||
|
if (n < static_cast<size_t>(TriangleStateType::Count))
|
||||||
|
data.used_states[n] = true;
|
||||||
|
|
||||||
if (n >= 3) {
|
if (n >= 3) {
|
||||||
assert(n <= 16);
|
assert(n <= 16);
|
||||||
if (n <= 16) {
|
if (n <= 16) {
|
||||||
// Store "11" plus 4 bits of (n-3).
|
// Store "11" plus 4 bits of (n-3).
|
||||||
data.second.insert(data.second.end(), { true, true });
|
data.bitstream.insert(data.bitstream.end(), { true, true });
|
||||||
n -= 3;
|
n -= 3;
|
||||||
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx)
|
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx)
|
||||||
data.second.push_back(n & (uint64_t(0b0001) << bit_idx));
|
data.bitstream.push_back(n & (uint64_t(0b0001) << bit_idx));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams.
|
// Simple case, compatible with PrusaSlicer 2.3.1 and older for storing paint on supports and seams.
|
||||||
// Store 2 bits of n.
|
// Store 2 bits of n.
|
||||||
data.second.push_back(n & 0b01);
|
data.bitstream.push_back(n & 0b01);
|
||||||
data.second.push_back(n & 0b10);
|
data.bitstream.push_back(n & 0b10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} out { this };
|
} out { this };
|
||||||
|
|
||||||
out.data.first.reserve(m_orig_size_indices);
|
out.data.triangles_to_split.reserve(m_orig_size_indices);
|
||||||
for (int i=0; i<m_orig_size_indices; ++i)
|
for (int i=0; i<m_orig_size_indices; ++i)
|
||||||
if (const Triangle& tr = m_triangles[i]; tr.is_split() || tr.get_state() != EnforcerBlockerType::NONE) {
|
if (const Triangle& tr = m_triangles[i]; tr.is_split() || tr.get_state() != TriangleStateType::NONE) {
|
||||||
// Store index of the first bit assigned to ith triangle.
|
// Store index of the first bit assigned to ith triangle.
|
||||||
out.data.first.emplace_back(i, int(out.data.second.size()));
|
out.data.triangles_to_split.emplace_back(i, int(out.data.bitstream.size()));
|
||||||
// out the triangle bits.
|
// out the triangle bits.
|
||||||
out.serialize(i);
|
out.serialize(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// May be stored onto Undo / Redo stack, thus conserve memory.
|
// May be stored onto Undo / Redo stack, thus conserve memory.
|
||||||
out.data.first.shrink_to_fit();
|
out.data.triangles_to_split.shrink_to_fit();
|
||||||
out.data.second.shrink_to_fit();
|
out.data.bitstream.shrink_to_fit();
|
||||||
return out.data;
|
return out.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, bool needs_reset)
|
void TriangleSelector::deserialize(const TriangleSplittingData &data, bool needs_reset) {
|
||||||
{
|
|
||||||
if (needs_reset)
|
if (needs_reset)
|
||||||
reset(); // dump any current state
|
reset(); // dump any current state
|
||||||
|
|
||||||
// Reserve number of triangles as if each triangle was saved with 4 bits.
|
// Reserve number of triangles as if each triangle was saved with 4 bits.
|
||||||
// With MMU painting this estimate may be somehow low, but better than nothing.
|
// With MMU painting this estimate may be somehow low, but better than nothing.
|
||||||
m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.second.size() / 4));
|
m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.bitstream.size() / 4));
|
||||||
// Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
|
// Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
|
||||||
// Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
|
// Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
|
||||||
m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2));
|
m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2));
|
||||||
@ -1627,13 +1619,13 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||||||
// kept outside of the loop to avoid re-allocating inside the loop.
|
// kept outside of the loop to avoid re-allocating inside the loop.
|
||||||
std::vector<ProcessingInfo> parents;
|
std::vector<ProcessingInfo> parents;
|
||||||
|
|
||||||
for (auto [triangle_id, ibit] : data.first) {
|
for (auto [triangle_id, ibit] : data.triangles_to_split) {
|
||||||
assert(triangle_id < int(m_triangles.size()));
|
assert(triangle_id < int(m_triangles.size()));
|
||||||
assert(ibit < int(data.second.size()));
|
assert(ibit < int(data.bitstream.size()));
|
||||||
auto next_nibble = [&data, &ibit = ibit]() {
|
auto next_nibble = [&data, &ibit = ibit]() {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (int i = 0; i < 4; ++ i)
|
for (int i = 0; i < 4; ++ i)
|
||||||
n |= data.second[ibit ++] << i;
|
n |= data.bitstream[ibit ++] << i;
|
||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1645,7 +1637,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||||||
int num_of_children = num_of_split_sides == 0 ? 0 : num_of_split_sides + 1;
|
int num_of_children = num_of_split_sides == 0 ? 0 : num_of_split_sides + 1;
|
||||||
bool is_split = num_of_children != 0;
|
bool is_split = num_of_children != 0;
|
||||||
// Only valid if not is_split. Value of the second nibble was subtracted by 3, so it is added back.
|
// Only valid if not is_split. Value of the second nibble was subtracted by 3, so it is added back.
|
||||||
auto state = is_split ? EnforcerBlockerType::NONE : EnforcerBlockerType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
auto state = is_split ? TriangleStateType::NONE : TriangleStateType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
||||||
// Only valid if is_split.
|
// Only valid if is_split.
|
||||||
int special_side = code >> 2;
|
int special_side = code >> 2;
|
||||||
|
|
||||||
@ -1657,7 +1649,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||||||
Vec3i neighbors = m_neighbors[triangle_id];
|
Vec3i neighbors = m_neighbors[triangle_id];
|
||||||
parents.push_back({triangle_id, neighbors, 0, num_of_children});
|
parents.push_back({triangle_id, neighbors, 0, num_of_children});
|
||||||
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
|
m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
|
||||||
perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
|
perform_split(triangle_id, neighbors, TriangleStateType::NONE);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// root is not split. just set the state and that's it.
|
// root is not split. just set the state and that's it.
|
||||||
@ -1677,7 +1669,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||||||
Vec3i neighbors = this->child_neighbors(tr, last.neighbors, child_idx);
|
Vec3i neighbors = this->child_neighbors(tr, last.neighbors, child_idx);
|
||||||
int this_idx = tr.children[child_idx];
|
int this_idx = tr.children[child_idx];
|
||||||
m_triangles[this_idx].set_division(num_of_split_sides, special_side);
|
m_triangles[this_idx].set_division(num_of_split_sides, special_side);
|
||||||
perform_split(this_idx, neighbors, EnforcerBlockerType::NONE);
|
perform_split(this_idx, neighbors, TriangleStateType::NONE);
|
||||||
parents.push_back({this_idx, neighbors, 0, num_of_children});
|
parents.push_back({this_idx, neighbors, 0, num_of_children});
|
||||||
} else {
|
} else {
|
||||||
// this triangle belongs to last split one
|
// this triangle belongs to last split one
|
||||||
@ -1705,21 +1697,53 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriangleSelector::TriangleSplittingData::update_used_states(const size_t bitstream_start_idx) {
|
||||||
|
assert(bitstream_start_idx < this->bitstream.size());
|
||||||
|
assert(!this->bitstream.empty() && this->bitstream.size() != bitstream_start_idx);
|
||||||
|
assert((this->bitstream.size() - bitstream_start_idx) % 4 == 0);
|
||||||
|
|
||||||
|
if (this->bitstream.empty() || this->bitstream.size() == bitstream_start_idx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t nibble_idx = bitstream_start_idx;
|
||||||
|
|
||||||
|
auto read_next_nibble = [&data_bitstream = std::as_const(this->bitstream), &nibble_idx]() -> uint8_t {
|
||||||
|
assert(nibble_idx + 3 < data_bitstream.size());
|
||||||
|
uint8_t code = 0;
|
||||||
|
for (size_t bit_idx = 0; bit_idx < 4; ++bit_idx)
|
||||||
|
code |= data_bitstream[nibble_idx++] << bit_idx;
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (nibble_idx < this->bitstream.size()) {
|
||||||
|
const uint8_t code = read_next_nibble();
|
||||||
|
|
||||||
|
if (const bool is_split = (code & 0b11) != 0; is_split)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const uint8_t facet_state = (code & 0b1100) == 0b1100 ? read_next_nibble() + 3 : code >> 2;
|
||||||
|
assert(facet_state < this->used_states.size());
|
||||||
|
if (facet_state >= this->used_states.size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this->used_states[facet_state] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lightweight variant of deserialization, which only tests whether a face of test_state exists.
|
// Lightweight variant of deserialization, which only tests whether a face of test_state exists.
|
||||||
bool TriangleSelector::has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, const EnforcerBlockerType test_state)
|
bool TriangleSelector::has_facets(const TriangleSplittingData &data, const TriangleStateType test_state) {
|
||||||
{
|
|
||||||
// Depth-first queue of a number of unvisited children.
|
// Depth-first queue of a number of unvisited children.
|
||||||
// Kept outside of the loop to avoid re-allocating inside the loop.
|
// Kept outside of the loop to avoid re-allocating inside the loop.
|
||||||
std::vector<int> parents_children;
|
std::vector<int> parents_children;
|
||||||
parents_children.reserve(64);
|
parents_children.reserve(64);
|
||||||
|
|
||||||
for (const std::pair<int, int> &triangle_id_and_ibit : data.first) {
|
for (const TriangleBitStreamMapping &triangle_id_and_ibit : data.triangles_to_split) {
|
||||||
int ibit = triangle_id_and_ibit.second;
|
int ibit = triangle_id_and_ibit.bitstream_start_idx;
|
||||||
assert(ibit < int(data.second.size()));
|
assert(ibit < int(data.bitstream.size()));
|
||||||
auto next_nibble = [&data, &ibit = ibit]() {
|
auto next_nibble = [&data, &ibit = ibit]() {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
for (int i = 0; i < 4; ++ i)
|
for (int i = 0; i < 4; ++ i)
|
||||||
n |= data.second[ibit ++] << i;
|
n |= data.bitstream[ibit ++] << i;
|
||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
// < 0 -> negative of a number of children
|
// < 0 -> negative of a number of children
|
||||||
@ -1764,8 +1788,7 @@ void TriangleSelector::seed_fill_unselect_all_triangles()
|
|||||||
triangle.unselect_by_seed_fill();
|
triangle.unselect_by_seed_fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_state)
|
void TriangleSelector::seed_fill_apply_on_triangles(TriangleStateType new_state) {
|
||||||
{
|
|
||||||
for (Triangle &triangle : m_triangles)
|
for (Triangle &triangle : m_triangles)
|
||||||
if (!triangle.is_split() && triangle.is_selected_by_seed_fill())
|
if (!triangle.is_split() && triangle.is_selected_by_seed_fill())
|
||||||
triangle.set_state(new_state);
|
triangle.set_state(new_state);
|
||||||
|
@ -14,8 +14,29 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
enum class EnforcerBlockerType : int8_t;
|
enum class TriangleStateType : int8_t {
|
||||||
|
// Maximum is 3. The value is serialized in TriangleSelector into 2 bits.
|
||||||
|
NONE = 0,
|
||||||
|
ENFORCER = 1,
|
||||||
|
BLOCKER = 2,
|
||||||
|
// Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code.
|
||||||
|
Extruder1 = ENFORCER,
|
||||||
|
Extruder2 = BLOCKER,
|
||||||
|
Extruder3,
|
||||||
|
Extruder4,
|
||||||
|
Extruder5,
|
||||||
|
Extruder6,
|
||||||
|
Extruder7,
|
||||||
|
Extruder8,
|
||||||
|
Extruder9,
|
||||||
|
Extruder10,
|
||||||
|
Extruder11,
|
||||||
|
Extruder12,
|
||||||
|
Extruder13,
|
||||||
|
Extruder14,
|
||||||
|
Extruder15,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
// Following class holds information about selected triangles. It also has power
|
// Following class holds information about selected triangles. It also has power
|
||||||
// to recursively subdivide the triangles and make the selection finer.
|
// to recursively subdivide the triangles and make the selection finer.
|
||||||
@ -184,6 +205,56 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TriangleBitStreamMapping
|
||||||
|
{
|
||||||
|
// Index of the triangle to which we assign the bitstream containing splitting information.
|
||||||
|
int triangle_idx = -1;
|
||||||
|
// Index of the first bit of the bitstream assigned to this triangle.
|
||||||
|
int bitstream_start_idx = -1;
|
||||||
|
|
||||||
|
TriangleBitStreamMapping() = default;
|
||||||
|
explicit TriangleBitStreamMapping(int triangleIdx, int bitstreamStartIdx) : triangle_idx(triangleIdx), bitstream_start_idx(bitstreamStartIdx) {}
|
||||||
|
|
||||||
|
friend bool operator==(const TriangleBitStreamMapping &lhs, const TriangleBitStreamMapping &rhs) { return lhs.triangle_idx == rhs.triangle_idx && lhs.bitstream_start_idx == rhs.bitstream_start_idx; }
|
||||||
|
friend bool operator!=(const TriangleBitStreamMapping &lhs, const TriangleBitStreamMapping &rhs) { return !(lhs == rhs); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class cereal::access;
|
||||||
|
template<class Archive> void serialize(Archive &ar) { ar(triangle_idx, bitstream_start_idx); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TriangleSplittingData {
|
||||||
|
// Vector of triangles and its indexes to the bitstream.
|
||||||
|
std::vector<TriangleBitStreamMapping> triangles_to_split;
|
||||||
|
// Bit stream containing splitting information.
|
||||||
|
std::vector<bool> bitstream;
|
||||||
|
// Array indicating which triangle state types are used (encoded inside bitstream).
|
||||||
|
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(TriangleStateType::Count), false) };
|
||||||
|
|
||||||
|
TriangleSplittingData() = default;
|
||||||
|
|
||||||
|
friend bool operator==(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) {
|
||||||
|
return lhs.triangles_to_split == rhs.triangles_to_split
|
||||||
|
&& lhs.bitstream == rhs.bitstream
|
||||||
|
&& lhs.used_states == rhs.used_states;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const TriangleSplittingData &lhs, const TriangleSplittingData &rhs) { return !(lhs == rhs); }
|
||||||
|
|
||||||
|
// Reset all used states before they are recomputed based on the bitstream.
|
||||||
|
void reset_used_states() {
|
||||||
|
used_states.resize(static_cast<size_t>(TriangleStateType::Count), false);
|
||||||
|
std::fill(used_states.begin(), used_states.end(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update used states based on the bitstream. It just iterated over the bitstream from the bitstream_start_idx till the end.
|
||||||
|
void update_used_states(size_t bitstream_start_idx);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class cereal::access;
|
||||||
|
template<class Archive> void serialize(Archive &ar) { ar(triangles_to_split, bitstream, used_states); }
|
||||||
|
};
|
||||||
|
|
||||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||||
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
||||||
|
|
||||||
@ -202,7 +273,7 @@ public:
|
|||||||
// Select all triangles fully inside the circle, subdivide where needed.
|
// Select all triangles fully inside the circle, subdivide where needed.
|
||||||
void select_patch(int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
void select_patch(int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||||
std::unique_ptr<Cursor> &&cursor, // Cursor containing information about the point where to start, camera position (mesh coords), matrix to get from mesh to world, and its shape and type.
|
std::unique_ptr<Cursor> &&cursor, // Cursor containing information about the point where to start, camera position (mesh coords), matrix to get from mesh to world, and its shape and type.
|
||||||
EnforcerBlockerType new_state, // enforcer or blocker?
|
TriangleStateType new_state, // enforcer or blocker?
|
||||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||||
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
||||||
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
||||||
@ -221,18 +292,18 @@ public:
|
|||||||
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
||||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||||
|
|
||||||
bool has_facets(EnforcerBlockerType state) const;
|
bool has_facets(TriangleStateType state) const;
|
||||||
static bool has_facets(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, EnforcerBlockerType test_state);
|
static bool has_facets(const TriangleSplittingData &data, TriangleStateType test_state);
|
||||||
int num_facets(EnforcerBlockerType state) const;
|
int num_facets(TriangleStateType state) const;
|
||||||
// Get facets at a given state. Don't triangulate T-joints.
|
// Get facets at a given state. Don't triangulate T-joints.
|
||||||
indexed_triangle_set get_facets(EnforcerBlockerType state) const;
|
indexed_triangle_set get_facets(TriangleStateType state) const;
|
||||||
// Get facets at a given state. Triangulate T-joints.
|
// Get facets at a given state. Triangulate T-joints.
|
||||||
indexed_triangle_set get_facets_strict(EnforcerBlockerType state) const;
|
indexed_triangle_set get_facets_strict(TriangleStateType state) const;
|
||||||
// Get edges around the selected area by seed fill.
|
// Get edges around the selected area by seed fill.
|
||||||
std::vector<Vec2i> get_seed_fill_contour() const;
|
std::vector<Vec2i> get_seed_fill_contour() const;
|
||||||
|
|
||||||
// Set facet of the mesh to a given state. Only works for original triangles.
|
// Set facet of the mesh to a given state. Only works for original triangles.
|
||||||
void set_facet(int facet_idx, EnforcerBlockerType state);
|
void set_facet(int facet_idx, TriangleStateType state);
|
||||||
|
|
||||||
// Clear everything and make the tree empty.
|
// Clear everything and make the tree empty.
|
||||||
void reset();
|
void reset();
|
||||||
@ -242,17 +313,20 @@ public:
|
|||||||
|
|
||||||
// Store the division trees in compact form (a long stream of bits for each triangle of the original mesh).
|
// Store the division trees in compact form (a long stream of bits for each triangle of the original mesh).
|
||||||
// First vector contains pairs of (triangle index, first bit in the second vector).
|
// First vector contains pairs of (triangle index, first bit in the second vector).
|
||||||
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> serialize() const;
|
TriangleSplittingData serialize() const;
|
||||||
|
|
||||||
// Load serialized data. Assumes that correct mesh is loaded.
|
// Load serialized data. Assumes that correct mesh is loaded.
|
||||||
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> &data, bool needs_reset = true);
|
void deserialize(const TriangleSplittingData &data, bool needs_reset = true);
|
||||||
|
|
||||||
|
// Extract all used facet states from the given TriangleSplittingData.
|
||||||
|
static std::vector<TriangleStateType> extract_used_facet_states(const TriangleSplittingData &data);
|
||||||
|
|
||||||
// For all triangles, remove the flag indicating that the triangle was selected by seed fill.
|
// For all triangles, remove the flag indicating that the triangle was selected by seed fill.
|
||||||
void seed_fill_unselect_all_triangles();
|
void seed_fill_unselect_all_triangles();
|
||||||
|
|
||||||
// For all triangles selected by seed fill, set new EnforcerBlockerType and remove flag indicating that triangle was selected by seed fill.
|
// For all triangles selected by seed fill, set new TriangleStateType and remove flag indicating that triangle was selected by seed fill.
|
||||||
// The operation may merge split triangles if they are being assigned the same color.
|
// The operation may merge split triangles if they are being assigned the same color.
|
||||||
void seed_fill_apply_on_triangles(EnforcerBlockerType new_state);
|
void seed_fill_apply_on_triangles(TriangleStateType new_state);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Triangle and info about how it's split.
|
// Triangle and info about how it's split.
|
||||||
@ -260,7 +334,7 @@ protected:
|
|||||||
public:
|
public:
|
||||||
// Use TriangleSelector::push_triangle to create a new triangle.
|
// Use TriangleSelector::push_triangle to create a new triangle.
|
||||||
// It increments/decrements reference counter on vertices.
|
// It increments/decrements reference counter on vertices.
|
||||||
Triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType init_state)
|
Triangle(int a, int b, int c, int source_triangle, const TriangleStateType init_state)
|
||||||
: verts_idxs{a, b, c},
|
: verts_idxs{a, b, c},
|
||||||
source_triangle{source_triangle},
|
source_triangle{source_triangle},
|
||||||
state{init_state}
|
state{init_state}
|
||||||
@ -282,8 +356,8 @@ protected:
|
|||||||
void set_division(int sides_to_split, int special_side_idx);
|
void set_division(int sides_to_split, int special_side_idx);
|
||||||
|
|
||||||
// Get/set current state.
|
// Get/set current state.
|
||||||
void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; }
|
void set_state(TriangleStateType type) { assert(! is_split()); state = type; }
|
||||||
EnforcerBlockerType get_state() const { assert(! is_split()); return state; }
|
TriangleStateType get_state() const { assert(! is_split()); return state; }
|
||||||
|
|
||||||
// Set if the triangle has been selected or unselected by seed fill.
|
// Set if the triangle has been selected or unselected by seed fill.
|
||||||
void select_by_seed_fill() { assert(! is_split()); m_selected_by_seed_fill = true; }
|
void select_by_seed_fill() { assert(! is_split()); m_selected_by_seed_fill = true; }
|
||||||
@ -307,7 +381,7 @@ protected:
|
|||||||
// or index of a vertex shared by the two split edges (for number_of_splits == 2).
|
// or index of a vertex shared by the two split edges (for number_of_splits == 2).
|
||||||
// For number_of_splits == 3, special_side_idx is always zero.
|
// For number_of_splits == 3, special_side_idx is always zero.
|
||||||
char special_side_idx { 0 };
|
char special_side_idx { 0 };
|
||||||
EnforcerBlockerType state;
|
TriangleStateType state;
|
||||||
bool m_selected_by_seed_fill : 1;
|
bool m_selected_by_seed_fill : 1;
|
||||||
// Is this triangle valid or marked to be removed?
|
// Is this triangle valid or marked to be removed?
|
||||||
bool m_valid : 1;
|
bool m_valid : 1;
|
||||||
@ -345,14 +419,14 @@ protected:
|
|||||||
|
|
||||||
// Private functions:
|
// Private functions:
|
||||||
private:
|
private:
|
||||||
bool select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting);
|
bool select_triangle(int facet_idx, TriangleStateType type, bool triangle_splitting);
|
||||||
bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType type, bool triangle_splitting);
|
bool select_triangle_recursive(int facet_idx, const Vec3i &neighbors, TriangleStateType type, bool triangle_splitting);
|
||||||
void undivide_triangle(int facet_idx);
|
void undivide_triangle(int facet_idx);
|
||||||
void split_triangle(int facet_idx, const Vec3i &neighbors);
|
void split_triangle(int facet_idx, const Vec3i &neighbors);
|
||||||
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
||||||
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
|
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
|
||||||
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
int push_triangle(int a, int b, int c, int source_triangle, TriangleStateType state = TriangleStateType::NONE);
|
||||||
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
void perform_split(int facet_idx, const Vec3i &neighbors, TriangleStateType old_state);
|
||||||
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
||||||
Vec3i child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors_propagated, int child_idx, const Vec3i &child_neighbors) const;
|
Vec3i child_neighbors_propagated(const Triangle &tr, const Vec3i &neighbors_propagated, int child_idx, const Vec3i &child_neighbors) const;
|
||||||
// Return child of itriangle at a CCW oriented side (vertexi, vertexj), either first or 2nd part.
|
// Return child of itriangle at a CCW oriented side (vertexi, vertexj), either first or 2nd part.
|
||||||
@ -381,7 +455,7 @@ private:
|
|||||||
void get_facets_strict_recursive(
|
void get_facets_strict_recursive(
|
||||||
const Triangle &tr,
|
const Triangle &tr,
|
||||||
const Vec3i &neighbors,
|
const Vec3i &neighbors,
|
||||||
EnforcerBlockerType state,
|
TriangleStateType state,
|
||||||
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||||
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
void get_facets_split_by_tjoints(const Vec3i &vertices, const Vec3i &neighbors, std::vector<stl_triangle_vertex_indices> &out_triangles) const;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig
|
|||||||
if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) {
|
if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) {
|
||||||
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
|
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
|
||||||
pos, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { });
|
pos, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { });
|
||||||
selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, trafo_no_translate,
|
selector.select_patch(hit.id, std::move(cursor), TriangleStateType::ENFORCER, trafo_no_translate,
|
||||||
true, eps_angle);
|
true, eps_angle);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig
|
|||||||
if (dist < radius) {
|
if (dist < radius) {
|
||||||
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
|
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(
|
||||||
point, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { });
|
point, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { });
|
||||||
selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER,
|
selector.select_patch(hit_idx_out, std::move(cursor), TriangleStateType::ENFORCER,
|
||||||
trafo_no_translate,
|
trafo_no_translate,
|
||||||
true, eps_angle);
|
true, eps_angle);
|
||||||
}
|
}
|
||||||
|
@ -628,11 +628,10 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
|
|||||||
m_layer_height_profile_modified = false;
|
m_layer_height_profile_modified = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::LayersEditing::update_slicing_parameters()
|
void GLCanvas3D::LayersEditing::update_slicing_parameters() {
|
||||||
{
|
if (m_slicing_parameters == nullptr) {
|
||||||
if (m_slicing_parameters == nullptr) {
|
m_slicing_parameters = new SlicingParameters();
|
||||||
m_slicing_parameters = new SlicingParameters();
|
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z, m_shrinkage_compensation);
|
||||||
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1605,9 +1604,11 @@ void GLCanvas3D::set_config(const DynamicPrintConfig* config)
|
|||||||
m_config = config;
|
m_config = config;
|
||||||
m_layers_editing.set_config(config);
|
m_layers_editing.set_config(config);
|
||||||
|
|
||||||
if (config) {
|
const PrinterTechnology ptech = current_printer_technology();
|
||||||
PrinterTechnology ptech = current_printer_technology();
|
if (const Print *print = fff_print(); ptech == ptFFF && print != nullptr)
|
||||||
|
m_layers_editing.set_shrinkage_compensation(fff_print()->shrinkage_compensation());
|
||||||
|
|
||||||
|
if (config) {
|
||||||
auto slot = ArrangeSettingsDb_AppCfg::slotFFF;
|
auto slot = ArrangeSettingsDb_AppCfg::slotFFF;
|
||||||
|
|
||||||
if (ptech == ptSLA) {
|
if (ptech == ptSLA) {
|
||||||
|
@ -219,6 +219,8 @@ class GLCanvas3D
|
|||||||
SlicingParameters *m_slicing_parameters{ nullptr };
|
SlicingParameters *m_slicing_parameters{ nullptr };
|
||||||
std::vector<double> m_layer_height_profile;
|
std::vector<double> m_layer_height_profile;
|
||||||
bool m_layer_height_profile_modified{ false };
|
bool m_layer_height_profile_modified{ false };
|
||||||
|
// Shrinkage compensation to apply when we need to use object_max_z with Z compensation.
|
||||||
|
Vec3d m_shrinkage_compensation{ Vec3d::Ones() };
|
||||||
|
|
||||||
mutable float m_adaptive_quality{ 0.5f };
|
mutable float m_adaptive_quality{ 0.5f };
|
||||||
mutable HeightProfileSmoothingParams m_smooth_params;
|
mutable HeightProfileSmoothingParams m_smooth_params;
|
||||||
@ -299,6 +301,8 @@ class GLCanvas3D
|
|||||||
|
|
||||||
std::pair<SlicingParameters, const std::vector<double>> get_layers_height_data();
|
std::pair<SlicingParameters, const std::vector<double>> get_layers_height_data();
|
||||||
|
|
||||||
|
void set_shrinkage_compensation(const Vec3d &shrinkage_compensation) { m_shrinkage_compensation = shrinkage_compensation; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_initialized() const;
|
bool is_initialized() const;
|
||||||
void generate_layer_height_texture();
|
void generate_layer_height_texture();
|
||||||
|
@ -374,7 +374,7 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
|
|||||||
const indexed_triangle_set &its = mv->mesh().its;
|
const indexed_triangle_set &its = mv->mesh().its;
|
||||||
for (const stl_triangle_vertex_indices &face : its.indices) {
|
for (const stl_triangle_vertex_indices &face : its.indices) {
|
||||||
if (its_face_normal(its, face).dot(down) > dot_limit) {
|
if (its_face_normal(its, face).dot(down) > dot_limit) {
|
||||||
m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER);
|
m_triangle_selectors[mesh_id]->set_facet(idx, block ? TriangleStateType::BLOCKER : TriangleStateType::ENFORCER);
|
||||||
m_triangle_selectors.back()->request_update_render_data();
|
m_triangle_selectors.back()->request_update_render_data();
|
||||||
}
|
}
|
||||||
++ idx;
|
++ idx;
|
||||||
|
@ -110,8 +110,8 @@ protected:
|
|||||||
ColorRGBA get_cursor_sphere_left_button_color() const override;
|
ColorRGBA get_cursor_sphere_left_button_color() const override;
|
||||||
ColorRGBA get_cursor_sphere_right_button_color() const override;
|
ColorRGBA get_cursor_sphere_right_button_color() const override;
|
||||||
|
|
||||||
EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType(m_first_selected_extruder_idx + 1); }
|
TriangleStateType get_left_button_state_type() const override { return TriangleStateType(m_first_selected_extruder_idx + 1); }
|
||||||
EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType(m_second_selected_extruder_idx + 1); }
|
TriangleStateType get_right_button_state_type() const override { return TriangleStateType(m_second_selected_extruder_idx + 1); }
|
||||||
|
|
||||||
void on_render_input_window(float x, float y, float bottom_limit) override;
|
void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||||
std::string on_get_name() const override;
|
std::string on_get_name() const override;
|
||||||
|
@ -503,7 +503,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
if (m_triangle_selectors.empty())
|
if (m_triangle_selectors.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EnforcerBlockerType new_state = EnforcerBlockerType::NONE;
|
TriangleStateType new_state = TriangleStateType::NONE;
|
||||||
if (! shift_down) {
|
if (! shift_down) {
|
||||||
if (action == SLAGizmoEventType::Dragging)
|
if (action == SLAGizmoEventType::Dragging)
|
||||||
new_state = m_button_down == Button::Left ? this->get_left_button_state_type() : this->get_right_button_state_type();
|
new_state = m_button_down == Button::Left ? this->get_left_button_state_type() : this->get_right_button_state_type();
|
||||||
@ -937,16 +937,16 @@ void TriangleSelectorGUI::update_render_data()
|
|||||||
static const float offset = 0.001f;
|
static const float offset = 0.001f;
|
||||||
|
|
||||||
for (const Triangle &tr : m_triangles) {
|
for (const Triangle &tr : m_triangles) {
|
||||||
if (!tr.valid() || tr.is_split() || (tr.get_state() == EnforcerBlockerType::NONE && !tr.is_selected_by_seed_fill()))
|
if (!tr.valid() || tr.is_split() || (tr.get_state() == TriangleStateType::NONE && !tr.is_selected_by_seed_fill()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int tr_state = int(tr.get_state());
|
int tr_state = int(tr.get_state());
|
||||||
GLModel::Geometry &iva = tr.is_selected_by_seed_fill() ? iva_seed_fills_data[tr_state] :
|
GLModel::Geometry &iva = tr.is_selected_by_seed_fill() ? iva_seed_fills_data[tr_state] :
|
||||||
tr.get_state() == EnforcerBlockerType::ENFORCER ? iva_enforcers_data :
|
tr.get_state() == TriangleStateType::ENFORCER ? iva_enforcers_data :
|
||||||
iva_blockers_data;
|
iva_blockers_data;
|
||||||
int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] :
|
int &cnt = tr.is_selected_by_seed_fill() ? seed_fill_cnt[tr_state] :
|
||||||
tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt :
|
tr.get_state() == TriangleStateType::ENFORCER ? enf_cnt :
|
||||||
blc_cnt;
|
blc_cnt;
|
||||||
const Vec3f &v0 = m_vertices[tr.verts_idxs[0]].v;
|
const Vec3f &v0 = m_vertices[tr.verts_idxs[0]].v;
|
||||||
const Vec3f &v1 = m_vertices[tr.verts_idxs[1]].v;
|
const Vec3f &v1 = m_vertices[tr.verts_idxs[1]].v;
|
||||||
const Vec3f &v2 = m_vertices[tr.verts_idxs[2]].v;
|
const Vec3f &v2 = m_vertices[tr.verts_idxs[2]].v;
|
||||||
|
@ -120,8 +120,8 @@ protected:
|
|||||||
virtual ColorRGBA get_cursor_sphere_left_button_color() const { return { 0.0f, 0.0f, 1.0f, 0.25f }; }
|
virtual ColorRGBA get_cursor_sphere_left_button_color() const { return { 0.0f, 0.0f, 1.0f, 0.25f }; }
|
||||||
virtual ColorRGBA get_cursor_sphere_right_button_color() const { return { 1.0f, 0.0f, 0.0f, 0.25f }; }
|
virtual ColorRGBA get_cursor_sphere_right_button_color() const { return { 1.0f, 0.0f, 0.0f, 0.25f }; }
|
||||||
|
|
||||||
virtual EnforcerBlockerType get_left_button_state_type() const { return EnforcerBlockerType::ENFORCER; }
|
virtual TriangleStateType get_left_button_state_type() const { return TriangleStateType::ENFORCER; }
|
||||||
virtual EnforcerBlockerType get_right_button_state_type() const { return EnforcerBlockerType::BLOCKER; }
|
virtual TriangleStateType get_right_button_state_type() const { return TriangleStateType::BLOCKER; }
|
||||||
|
|
||||||
float m_cursor_radius = 2.f;
|
float m_cursor_radius = 2.f;
|
||||||
static constexpr float CursorRadiusMin = 0.4f; // cannot be zero
|
static constexpr float CursorRadiusMin = 0.4f; // cannot be zero
|
||||||
|
@ -136,6 +136,10 @@ enum class NotificationType
|
|||||||
SelectFilamentFromConnect,
|
SelectFilamentFromConnect,
|
||||||
// Debug notification for connect communication
|
// Debug notification for connect communication
|
||||||
PrusaConnectPrinters,
|
PrusaConnectPrinters,
|
||||||
|
// Notification that bed temperatures for the used filaments differ significantly.
|
||||||
|
BedTemperaturesDiffer,
|
||||||
|
// Notification that shrinkage compensations for the used filaments differ.
|
||||||
|
ShrinkageCompensationsDiffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
class NotificationManager
|
class NotificationManager
|
||||||
|
@ -1916,9 +1916,14 @@ void Plater::priv::process_validation_warning(const std::vector<std::string>& wa
|
|||||||
if (warnings.empty())
|
if (warnings.empty())
|
||||||
notification_manager->close_notification_of_type(NotificationType::ValidateWarning);
|
notification_manager->close_notification_of_type(NotificationType::ValidateWarning);
|
||||||
|
|
||||||
for (std::string text : warnings) {
|
// Always close warnings BedTemperaturesDiffer and ShrinkageCompensationsDiffer before next processing.
|
||||||
std::string hypertext = "";
|
notification_manager->close_notification_of_type(NotificationType::BedTemperaturesDiffer);
|
||||||
std::function<bool(wxEvtHandler*)> action_fn = [](wxEvtHandler*){ return false; };
|
notification_manager->close_notification_of_type(NotificationType::ShrinkageCompensationsDiffer);
|
||||||
|
|
||||||
|
for (std::string text : warnings) {
|
||||||
|
std::string hypertext = "";
|
||||||
|
NotificationType notification_type = NotificationType::ValidateWarning;
|
||||||
|
std::function<bool(wxEvtHandler*)> action_fn = [](wxEvtHandler*){ return false; };
|
||||||
|
|
||||||
if (text == "_SUPPORTS_OFF") {
|
if (text == "_SUPPORTS_OFF") {
|
||||||
text = _u8L("An object has custom support enforcers which will not be used "
|
text = _u8L("An object has custom support enforcers which will not be used "
|
||||||
@ -1934,12 +1939,17 @@ void Plater::priv::process_validation_warning(const std::vector<std::string>& wa
|
|||||||
print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto"));
|
print_tab->on_value_change("support_material_auto", config.opt_bool("support_material_auto"));
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
} else if (text == "_BED_TEMPS_DIFFER") {
|
||||||
|
text = _u8L("Bed temperatures for the used filaments differ significantly.");
|
||||||
|
notification_type = NotificationType::BedTemperaturesDiffer;
|
||||||
|
} else if (text == "_FILAMENT_SHRINKAGE_DIFFER") {
|
||||||
|
text = _u8L("Filament shrinkage will not be used because filament shrinkage "
|
||||||
|
"for the used filaments differs significantly.");
|
||||||
|
notification_type = NotificationType::ShrinkageCompensationsDiffer;
|
||||||
}
|
}
|
||||||
if (text == "_BED_TEMPS_DIFFER")
|
|
||||||
text = _u8L("Bed temperatures for the used filaments differ significantly.");
|
|
||||||
|
|
||||||
notification_manager->push_notification(
|
notification_manager->push_notification(
|
||||||
NotificationType::ValidateWarning,
|
notification_type,
|
||||||
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||||
_u8L("WARNING:") + "\n" + text, hypertext, action_fn
|
_u8L("WARNING:") + "\n" + text, hypertext, action_fn
|
||||||
);
|
);
|
||||||
|
@ -2237,6 +2237,10 @@ void TabFilament::build()
|
|||||||
};
|
};
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
|
|
||||||
|
optgroup = page->new_optgroup(L("Shrinkage compensation"));
|
||||||
|
optgroup->append_single_option_line("filament_shrinkage_compensation_xy");
|
||||||
|
optgroup->append_single_option_line("filament_shrinkage_compensation_z");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Wipe tower parameters"));
|
optgroup = page->new_optgroup(L("Wipe tower parameters"));
|
||||||
optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
|
optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user