mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-09 18:27:00 +00:00
added support for multi perimeter patterns, issue 34
This commit is contained in:
@@ -170,7 +170,9 @@ public:
|
|||||||
, m_can_reverse(rhs.m_can_reverse)
|
, m_can_reverse(rhs.m_can_reverse)
|
||||||
, m_role(rhs.m_role)
|
, m_role(rhs.m_role)
|
||||||
, m_no_extrusion(rhs.m_no_extrusion)
|
, m_no_extrusion(rhs.m_no_extrusion)
|
||||||
{}
|
{
|
||||||
|
this->inset_idx = rhs.inset_idx;
|
||||||
|
}
|
||||||
ExtrusionPath(ExtrusionPath &&rhs)
|
ExtrusionPath(ExtrusionPath &&rhs)
|
||||||
: polyline(std::move(rhs.polyline))
|
: polyline(std::move(rhs.polyline))
|
||||||
, mm3_per_mm(rhs.mm3_per_mm)
|
, mm3_per_mm(rhs.mm3_per_mm)
|
||||||
@@ -179,7 +181,9 @@ public:
|
|||||||
, m_can_reverse(rhs.m_can_reverse)
|
, m_can_reverse(rhs.m_can_reverse)
|
||||||
, m_role(rhs.m_role)
|
, m_role(rhs.m_role)
|
||||||
, m_no_extrusion(rhs.m_no_extrusion)
|
, m_no_extrusion(rhs.m_no_extrusion)
|
||||||
{}
|
{
|
||||||
|
this->inset_idx = rhs.inset_idx;
|
||||||
|
}
|
||||||
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs)
|
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs)
|
||||||
: polyline(polyline)
|
: polyline(polyline)
|
||||||
, mm3_per_mm(rhs.mm3_per_mm)
|
, mm3_per_mm(rhs.mm3_per_mm)
|
||||||
@@ -188,7 +192,9 @@ public:
|
|||||||
, m_can_reverse(rhs.m_can_reverse)
|
, m_can_reverse(rhs.m_can_reverse)
|
||||||
, m_role(rhs.m_role)
|
, m_role(rhs.m_role)
|
||||||
, m_no_extrusion(rhs.m_no_extrusion)
|
, m_no_extrusion(rhs.m_no_extrusion)
|
||||||
{}
|
{
|
||||||
|
this->inset_idx = rhs.inset_idx;
|
||||||
|
}
|
||||||
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs)
|
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs)
|
||||||
: polyline(std::move(polyline))
|
: polyline(std::move(polyline))
|
||||||
, mm3_per_mm(rhs.mm3_per_mm)
|
, mm3_per_mm(rhs.mm3_per_mm)
|
||||||
@@ -197,7 +203,9 @@ public:
|
|||||||
, m_can_reverse(rhs.m_can_reverse)
|
, m_can_reverse(rhs.m_can_reverse)
|
||||||
, m_role(rhs.m_role)
|
, m_role(rhs.m_role)
|
||||||
, m_no_extrusion(rhs.m_no_extrusion)
|
, m_no_extrusion(rhs.m_no_extrusion)
|
||||||
{}
|
{
|
||||||
|
this->inset_idx = rhs.inset_idx;
|
||||||
|
}
|
||||||
|
|
||||||
ExtrusionPath& operator=(const ExtrusionPath& rhs) {
|
ExtrusionPath& operator=(const ExtrusionPath& rhs) {
|
||||||
m_can_reverse = rhs.m_can_reverse;
|
m_can_reverse = rhs.m_can_reverse;
|
||||||
@@ -207,6 +215,7 @@ public:
|
|||||||
this->width = rhs.width;
|
this->width = rhs.width;
|
||||||
this->height = rhs.height;
|
this->height = rhs.height;
|
||||||
this->polyline = rhs.polyline;
|
this->polyline = rhs.polyline;
|
||||||
|
this->inset_idx = rhs.inset_idx;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
ExtrusionPath& operator=(ExtrusionPath&& rhs) {
|
ExtrusionPath& operator=(ExtrusionPath&& rhs) {
|
||||||
@@ -217,6 +226,7 @@ public:
|
|||||||
this->width = rhs.width;
|
this->width = rhs.width;
|
||||||
this->height = rhs.height;
|
this->height = rhs.height;
|
||||||
this->polyline = std::move(rhs.polyline);
|
this->polyline = std::move(rhs.polyline);
|
||||||
|
this->inset_idx = rhs.inset_idx;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4008,6 +4008,53 @@ static bool split_extrusion_collection_for_pointillism_paths(
|
|||||||
return out_stats.segment_count > 0;
|
return out_stats.segment_count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool split_extrusion_collection_for_multi_perimeter_pattern(
|
||||||
|
const ExtrusionEntityCollection& source,
|
||||||
|
const MixedFilamentManager& mixed_mgr,
|
||||||
|
unsigned int mixed_filament_id,
|
||||||
|
size_t num_physical,
|
||||||
|
int layer_index,
|
||||||
|
std::vector<std::unique_ptr<ExtrusionEntityCollection>>& out_by_extruder,
|
||||||
|
size_t& out_bucket_count)
|
||||||
|
{
|
||||||
|
out_by_extruder.clear();
|
||||||
|
out_by_extruder.resize(num_physical);
|
||||||
|
out_bucket_count = 0;
|
||||||
|
|
||||||
|
if (source.entities.empty() || num_physical == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t split_entities = 0;
|
||||||
|
ExtrusionEntityCollection flattened = source.flatten(false);
|
||||||
|
for (const ExtrusionEntity* entity : flattened.entities) {
|
||||||
|
if (entity == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int perimeter_index = entity->inset_idx;
|
||||||
|
if (perimeter_index < 0)
|
||||||
|
perimeter_index = entity->role() == erExternalPerimeter ? 0 : 1;
|
||||||
|
|
||||||
|
const unsigned int extruder_id = mixed_mgr.resolve_perimeter(
|
||||||
|
mixed_filament_id, num_physical, layer_index, perimeter_index);
|
||||||
|
if (extruder_id == 0 || extruder_id > num_physical)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::unique_ptr<ExtrusionEntityCollection>& bucket = out_by_extruder[extruder_id - 1];
|
||||||
|
if (!bucket) {
|
||||||
|
bucket = std::make_unique<ExtrusionEntityCollection>();
|
||||||
|
bucket->no_sort = source.no_sort;
|
||||||
|
}
|
||||||
|
bucket->append(*entity);
|
||||||
|
++split_entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const std::unique_ptr<ExtrusionEntityCollection>& bucket : out_by_extruder) {
|
||||||
|
if (bucket && !bucket->entities.empty())
|
||||||
|
++out_bucket_count;
|
||||||
|
}
|
||||||
|
return split_entities > 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline std::vector<GCode::ObjectByExtruder::Island>& object_islands_by_extruder(
|
inline std::vector<GCode::ObjectByExtruder::Island>& object_islands_by_extruder(
|
||||||
std::map<unsigned int, std::vector<GCode::ObjectByExtruder>>& by_extruder,
|
std::map<unsigned int, std::vector<GCode::ObjectByExtruder>>& by_extruder,
|
||||||
unsigned int extruder_id,
|
unsigned int extruder_id,
|
||||||
@@ -4750,6 +4797,27 @@ LayerResult GCode::process_layer(const Print& print,
|
|||||||
auto inserted = pointillism_sequence_cache.emplace(filament_id_1based, std::move(sequence));
|
auto inserted = pointillism_sequence_cache.emplace(filament_id_1based, std::move(sequence));
|
||||||
return inserted.first->second.empty() ? nullptr : &inserted.first->second;
|
return inserted.first->second.empty() ? nullptr : &inserted.first->second;
|
||||||
};
|
};
|
||||||
|
auto grouped_manual_pattern_mixed_filament_id =
|
||||||
|
[&layer_tools, &configured_filament_id_1based](const ExtrusionEntityCollection& entities,
|
||||||
|
const PrintRegion& region) -> unsigned int {
|
||||||
|
if (layer_tools.mixed_mgr == nullptr || layer_tools.num_physical == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto has_grouped_pattern = [&layer_tools](unsigned int filament_id_1based) -> bool {
|
||||||
|
if (!layer_tools.mixed_mgr->is_mixed(filament_id_1based, layer_tools.num_physical))
|
||||||
|
return false;
|
||||||
|
const MixedFilament* mixed_row = layer_tools.mixed_mgr->mixed_filament_from_id(filament_id_1based, layer_tools.num_physical);
|
||||||
|
if (mixed_row == nullptr)
|
||||||
|
return false;
|
||||||
|
const std::string normalized_pattern = MixedFilamentManager::normalize_manual_pattern(mixed_row->manual_pattern);
|
||||||
|
return normalized_pattern.find(',') != std::string::npos;
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned int configured_filament_id = configured_filament_id_1based(entities, region);
|
||||||
|
if (has_grouped_pattern(configured_filament_id))
|
||||||
|
return configured_filament_id;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
// Compensate perimeter clipping at mixed-mask boundaries to avoid cracks from exact centerline clipping.
|
// Compensate perimeter clipping at mixed-mask boundaries to avoid cracks from exact centerline clipping.
|
||||||
constexpr double LOCAL_Z_PERIMETER_MASK_EXPAND_MM = 0.10;
|
constexpr double LOCAL_Z_PERIMETER_MASK_EXPAND_MM = 0.10;
|
||||||
// Keep base exclusion smaller than mixed-pass inclusion to guarantee a slight overlap
|
// Keep base exclusion smaller than mixed-pass inclusion to guarantee a slight overlap
|
||||||
@@ -5216,6 +5284,47 @@ LayerResult GCode::process_layer(const Print& print,
|
|||||||
|
|
||||||
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
|
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
|
||||||
int correct_extruder_id = layer_tools.extruder(*filtered_extrusions, region);
|
int correct_extruder_id = layer_tools.extruder(*filtered_extrusions, region);
|
||||||
|
if (!is_anything_overridden &&
|
||||||
|
entity_type == ObjectByExtruder::Island::Region::PERIMETERS &&
|
||||||
|
layer_tools.mixed_mgr != nullptr &&
|
||||||
|
layer_tools.num_physical > 0 &&
|
||||||
|
correct_extruder_id >= 0) {
|
||||||
|
const unsigned int mixed_filament_id =
|
||||||
|
grouped_manual_pattern_mixed_filament_id(*filtered_extrusions, region);
|
||||||
|
if (mixed_filament_id != 0) {
|
||||||
|
std::vector<std::unique_ptr<ExtrusionEntityCollection>> split_by_extruder;
|
||||||
|
size_t bucket_count = 0;
|
||||||
|
if (split_extrusion_collection_for_multi_perimeter_pattern(*filtered_extrusions,
|
||||||
|
*layer_tools.mixed_mgr,
|
||||||
|
mixed_filament_id,
|
||||||
|
layer_tools.num_physical,
|
||||||
|
layer_tools.layer_index,
|
||||||
|
split_by_extruder,
|
||||||
|
bucket_count) &&
|
||||||
|
bucket_count >= 2) {
|
||||||
|
for (size_t extruder_idx = 0; extruder_idx < split_by_extruder.size(); ++extruder_idx) {
|
||||||
|
std::unique_ptr<ExtrusionEntityCollection>& split_collection = split_by_extruder[extruder_idx];
|
||||||
|
if (!split_collection || split_collection->entities.empty())
|
||||||
|
continue;
|
||||||
|
const ExtrusionEntityCollection* split_ptr = split_collection.get();
|
||||||
|
local_z_clipped_collections.emplace_back(std::move(split_collection));
|
||||||
|
std::vector<ObjectByExtruder::Island>& islands =
|
||||||
|
object_islands_by_extruder(by_extruder, unsigned(extruder_idx), layer_to_print_idx, layers.size(), n_slices + 1);
|
||||||
|
for (size_t i = 0; i <= n_slices; ++i) {
|
||||||
|
const bool last = i == n_slices;
|
||||||
|
const size_t island_idx = last ? n_slices : slices_test_order[i];
|
||||||
|
if (last || point_inside_surface(island_idx, split_ptr->first_point())) {
|
||||||
|
if (islands[island_idx].by_region.empty())
|
||||||
|
islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
|
||||||
|
islands[island_idx].by_region[region.print_region_id()].append(entity_type, split_ptr, nullptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Let's recover vector of extruder overrides:
|
// Let's recover vector of extruder overrides:
|
||||||
const WipingExtrusions::ExtruderPerCopy* entity_overrides = nullptr;
|
const WipingExtrusions::ExtruderPerCopy* entity_overrides = nullptr;
|
||||||
|
|||||||
@@ -60,6 +60,45 @@ unsigned int resolve_mixed_with_layer_heights(const MixedFilamentManager *mixed_
|
|||||||
return mixed_mgr->resolve(filament_id_1based, num_physical, layer_index, layer_print_z, layer_height);
|
return mixed_mgr->resolve(filament_id_1based, num_physical, layer_index, layer_print_z, layer_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_grouped_manual_pattern(const MixedFilamentManager *mixed_mgr,
|
||||||
|
size_t num_physical,
|
||||||
|
unsigned int filament_id_1based)
|
||||||
|
{
|
||||||
|
if (!(mixed_mgr && mixed_mgr->is_mixed(filament_id_1based, num_physical)))
|
||||||
|
return false;
|
||||||
|
const MixedFilament *mixed_row = mixed_mgr->mixed_filament_from_id(filament_id_1based, num_physical);
|
||||||
|
if (mixed_row == nullptr)
|
||||||
|
return false;
|
||||||
|
const std::string normalized = MixedFilamentManager::normalize_manual_pattern(mixed_row->manual_pattern);
|
||||||
|
return normalized.find(',') != std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_unique_preserve_order(std::vector<unsigned int> &dst, unsigned int value)
|
||||||
|
{
|
||||||
|
if (std::find(dst.begin(), dst.end(), value) == dst.end())
|
||||||
|
dst.emplace_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int grouped_manual_pattern_mixed_filament_id_for_layer(const LayerTools& layer_tools,
|
||||||
|
unsigned int configured_filament_id_1based)
|
||||||
|
{
|
||||||
|
if (layer_tools.mixed_mgr == nullptr || layer_tools.num_physical == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (has_grouped_manual_pattern(layer_tools.mixed_mgr, layer_tools.num_physical, configured_filament_id_1based))
|
||||||
|
return configured_filament_id_1based;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_duplicates_preserve_order(std::vector<unsigned int> &values)
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> ordered;
|
||||||
|
ordered.reserve(values.size());
|
||||||
|
for (unsigned int value : values)
|
||||||
|
append_unique_preserve_order(ordered, value);
|
||||||
|
values = std::move(ordered);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
@@ -639,11 +678,34 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (something_nonoverriddable){
|
if (something_nonoverriddable){
|
||||||
unsigned int wall_ext = (extruder_override == 0) ? region.config().wall_filament.value : extruder_override;
|
const unsigned int configured_wall = (extruder_override == 0) ? region.config().wall_filament.value : extruder_override;
|
||||||
wall_ext = resolve_mixed(wall_ext, layerCount, float(layer->print_z), float(layer->height));
|
unsigned int wall_ext = resolve_mixed(configured_wall, layerCount, float(layer->print_z), float(layer->height));
|
||||||
layer_tools.extruders.emplace_back(wall_ext);
|
const unsigned int grouped_id =
|
||||||
if (layerCount == 0) {
|
grouped_manual_pattern_mixed_filament_id_for_layer(layer_tools, configured_wall);
|
||||||
firstLayerExtruders.emplace_back(wall_ext);
|
if (grouped_id != 0) {
|
||||||
|
const std::vector<unsigned int> ordered =
|
||||||
|
m_mixed_mgr->ordered_perimeter_extruders(grouped_id,
|
||||||
|
m_num_physical,
|
||||||
|
layerCount,
|
||||||
|
float(layer->print_z),
|
||||||
|
float(layer->height));
|
||||||
|
if (ordered.size() >= 2) {
|
||||||
|
layer_tools.preserve_extruder_order = true;
|
||||||
|
for (unsigned int extruder_id : ordered) {
|
||||||
|
layer_tools.extruders.emplace_back(extruder_id);
|
||||||
|
if (layerCount == 0 &&
|
||||||
|
std::find(firstLayerExtruders.begin(), firstLayerExtruders.end(), int(extruder_id)) == firstLayerExtruders.end())
|
||||||
|
firstLayerExtruders.emplace_back(int(extruder_id));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layer_tools.extruders.emplace_back(wall_ext);
|
||||||
|
if (layerCount == 0)
|
||||||
|
firstLayerExtruders.emplace_back(wall_ext);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layer_tools.extruders.emplace_back(wall_ext);
|
||||||
|
if (layerCount == 0)
|
||||||
|
firstLayerExtruders.emplace_back(wall_ext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,8 +758,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||||||
const_cast<PrintObject&>(object).object_first_layer_wall_extruders = firstLayerExtruders;
|
const_cast<PrintObject&>(object).object_first_layer_wall_extruders = firstLayerExtruders;
|
||||||
|
|
||||||
for (auto& layer : m_layer_tools) {
|
for (auto& layer : m_layer_tools) {
|
||||||
// Sort and remove duplicates
|
if (layer.preserve_extruder_order)
|
||||||
sort_remove_duplicates(layer.extruders);
|
remove_duplicates_preserve_order(layer.extruders);
|
||||||
|
else
|
||||||
|
sort_remove_duplicates(layer.extruders);
|
||||||
|
|
||||||
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
|
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
|
||||||
if (layer.extruders.empty() && layer.has_object)
|
if (layer.extruders.empty() && layer.has_object)
|
||||||
@@ -739,6 +803,10 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
|
|||||||
if (lt.extruders.front() == 0)
|
if (lt.extruders.front() == 0)
|
||||||
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
||||||
lt.extruders.erase(lt.extruders.begin());
|
lt.extruders.erase(lt.extruders.begin());
|
||||||
|
if (lt.preserve_extruder_order) {
|
||||||
|
last_extruder_id = lt.extruders.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Reorder the extruders to start with the last one.
|
// Reorder the extruders to start with the last one.
|
||||||
for (size_t i = 1; i < lt.extruders.size(); ++ i)
|
for (size_t i = 1; i < lt.extruders.size(); ++ i)
|
||||||
if (lt.extruders[i] == last_extruder_id) {
|
if (lt.extruders[i] == last_extruder_id) {
|
||||||
@@ -789,27 +857,29 @@ void ToolOrdering::reorder_extruders(std::vector<unsigned int> tool_order_layer0
|
|||||||
// Reorder the extruders of first layer
|
// Reorder the extruders of first layer
|
||||||
{
|
{
|
||||||
LayerTools& lt = m_layer_tools[0];
|
LayerTools& lt = m_layer_tools[0];
|
||||||
std::vector<unsigned int> layer0_extruders = lt.extruders;
|
if (!lt.preserve_extruder_order) {
|
||||||
lt.extruders.clear();
|
std::vector<unsigned int> layer0_extruders = lt.extruders;
|
||||||
for (unsigned int extruder_id : tool_order_layer0) {
|
lt.extruders.clear();
|
||||||
auto iter = std::find(layer0_extruders.begin(), layer0_extruders.end(), extruder_id);
|
for (unsigned int extruder_id : tool_order_layer0) {
|
||||||
if (iter != layer0_extruders.end()) {
|
auto iter = std::find(layer0_extruders.begin(), layer0_extruders.end(), extruder_id);
|
||||||
lt.extruders.push_back(extruder_id);
|
if (iter != layer0_extruders.end()) {
|
||||||
*iter = (unsigned int)-1;
|
lt.extruders.push_back(extruder_id);
|
||||||
|
*iter = (unsigned int)-1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int extruder_id : layer0_extruders) {
|
for (unsigned int extruder_id : layer0_extruders) {
|
||||||
if (extruder_id == 0)
|
if (extruder_id == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (extruder_id != (unsigned int)-1)
|
if (extruder_id != (unsigned int)-1)
|
||||||
lt.extruders.push_back(extruder_id);
|
lt.extruders.push_back(extruder_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// all extruders are zero
|
// all extruders are zero
|
||||||
if (lt.extruders.empty()) {
|
if (lt.extruders.empty()) {
|
||||||
lt.extruders.push_back(tool_order_layer0[0]);
|
lt.extruders.push_back(tool_order_layer0[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,6 +895,10 @@ void ToolOrdering::reorder_extruders(std::vector<unsigned int> tool_order_layer0
|
|||||||
if (lt.extruders.front() == 0)
|
if (lt.extruders.front() == 0)
|
||||||
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
||||||
lt.extruders.erase(lt.extruders.begin());
|
lt.extruders.erase(lt.extruders.begin());
|
||||||
|
if (lt.preserve_extruder_order) {
|
||||||
|
last_extruder_id = lt.extruders.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Reorder the extruders to start with the last one.
|
// Reorder the extruders to start with the last one.
|
||||||
for (size_t i = 1; i < lt.extruders.size(); ++i)
|
for (size_t i = 1; i < lt.extruders.size(); ++i)
|
||||||
if (lt.extruders[i] == last_extruder_id) {
|
if (lt.extruders[i] == last_extruder_id) {
|
||||||
@@ -1039,6 +1113,10 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume()
|
|||||||
LayerTools& lt = m_layer_tools[i];
|
LayerTools& lt = m_layer_tools[i];
|
||||||
if (lt.extruders.empty())
|
if (lt.extruders.empty())
|
||||||
continue;
|
continue;
|
||||||
|
if (lt.preserve_extruder_order) {
|
||||||
|
current_extruder_id = lt.extruders.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<int> custom_extruder_seq;
|
std::vector<int> custom_extruder_seq;
|
||||||
if (get_custom_seq(i, custom_extruder_seq) && !custom_extruder_seq.empty()) {
|
if (get_custom_seq(i, custom_extruder_seq) && !custom_extruder_seq.empty()) {
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ public:
|
|||||||
bool has_support = false;
|
bool has_support = false;
|
||||||
// Zero based extruder IDs, ordered to minimize tool switches.
|
// Zero based extruder IDs, ordered to minimize tool switches.
|
||||||
std::vector<unsigned int> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
|
bool preserve_extruder_order = false;
|
||||||
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
|
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
|
||||||
// If not overriden, it is set to 0.
|
// If not overriden, it is set to 0.
|
||||||
unsigned int extruder_override = 0;
|
unsigned int extruder_override = 0;
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ static bool parse_row_definition(const std::string &row,
|
|||||||
while (std::getline(ss, token, ','))
|
while (std::getline(ss, token, ','))
|
||||||
tokens.emplace_back(trim_copy(token));
|
tokens.emplace_back(trim_copy(token));
|
||||||
|
|
||||||
if (tokens.size() < 4 || tokens.size() > 13)
|
if (tokens.size() < 4)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int values[5] = { 0, 0, 1, 1, 50 };
|
int values[5] = { 0, 0, 1, 1, 50 };
|
||||||
@@ -436,6 +436,10 @@ static bool parse_row_definition(const std::string &row,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> pattern_tokens;
|
||||||
|
pattern_tokens.reserve(tokens.size() > token_idx ? tokens.size() - token_idx : 1);
|
||||||
|
if (!manual_pattern.empty())
|
||||||
|
pattern_tokens.push_back(manual_pattern);
|
||||||
for (size_t i = token_idx; i < tokens.size(); ++i) {
|
for (size_t i = token_idx; i < tokens.size(); ++i) {
|
||||||
const std::string &tok = tokens[i];
|
const std::string &tok = tokens[i];
|
||||||
if (tok.empty())
|
if (tok.empty())
|
||||||
@@ -472,7 +476,17 @@ static bool parse_row_definition(const std::string &row,
|
|||||||
stable_id = parsed_stable_id;
|
stable_id = parsed_stable_id;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
manual_pattern = tok;
|
pattern_tokens.push_back(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pattern_tokens.empty()) {
|
||||||
|
std::ostringstream joined_pattern;
|
||||||
|
for (size_t i = 0; i < pattern_tokens.size(); ++i) {
|
||||||
|
if (i != 0)
|
||||||
|
joined_pattern << ',';
|
||||||
|
joined_pattern << pattern_tokens[i];
|
||||||
|
}
|
||||||
|
manual_pattern = joined_pattern.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compatibility for early same-layer prototype rows.
|
// Compatibility for early same-layer prototype rows.
|
||||||
@@ -504,14 +518,69 @@ static bool decode_pattern_step(char c, char &out)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> split_manual_pattern_groups(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::vector<std::string> groups;
|
||||||
|
if (pattern.empty())
|
||||||
|
return groups;
|
||||||
|
|
||||||
|
std::string current;
|
||||||
|
for (const char c : pattern) {
|
||||||
|
if (c == ',') {
|
||||||
|
if (!current.empty()) {
|
||||||
|
groups.emplace_back(std::move(current));
|
||||||
|
current.clear();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current.push_back(c);
|
||||||
|
}
|
||||||
|
if (!current.empty())
|
||||||
|
groups.emplace_back(std::move(current));
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string flatten_manual_pattern_groups(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::string flattened;
|
||||||
|
flattened.reserve(pattern.size());
|
||||||
|
for (const char c : pattern)
|
||||||
|
if (c != ',')
|
||||||
|
flattened.push_back(c);
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int physical_filament_from_pattern_step(char token, const MixedFilament &mf, size_t num_physical)
|
||||||
|
{
|
||||||
|
if (token == '1')
|
||||||
|
return mf.component_a;
|
||||||
|
if (token == '2')
|
||||||
|
return mf.component_b;
|
||||||
|
if (token >= '3' && token <= '9') {
|
||||||
|
const unsigned int direct = unsigned(token - '0');
|
||||||
|
if (direct >= 1 && direct <= num_physical)
|
||||||
|
return direct;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mix_percent_from_normalized_pattern(const std::string &pattern)
|
static int mix_percent_from_normalized_pattern(const std::string &pattern)
|
||||||
{
|
{
|
||||||
if (pattern.empty())
|
const std::vector<std::string> groups = split_manual_pattern_groups(pattern);
|
||||||
|
if (groups.empty())
|
||||||
return 50;
|
return 50;
|
||||||
// Legacy blend ratio for UI preview: count component-B aliases only.
|
|
||||||
// Tokens '3'..'9' are direct physical filament IDs and are ignored here.
|
// For grouped patterns, blend preview is the average of each perimeter
|
||||||
const int count_b = int(std::count(pattern.begin(), pattern.end(), '2'));
|
// group's own cadence. This keeps simple outer/inner patterns like
|
||||||
return clamp_int(int(std::lround(100.0 * double(count_b) / double(pattern.size()))), 0, 100);
|
// "12,21" at 50/50 and "11111112,11121111" at 12.5%.
|
||||||
|
double blend_b = 0.0;
|
||||||
|
for (const std::string &group : groups) {
|
||||||
|
if (group.empty())
|
||||||
|
continue;
|
||||||
|
const int count_b = int(std::count(group.begin(), group.end(), '2'));
|
||||||
|
blend_b += double(count_b) / double(group.size());
|
||||||
|
}
|
||||||
|
return clamp_int(int(std::lround(100.0 * blend_b / double(groups.size()))), 0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string normalize_gradient_component_ids(const std::string &components)
|
static std::string normalize_gradient_component_ids(const std::string &components)
|
||||||
@@ -859,10 +928,19 @@ std::string MixedFilamentManager::normalize_manual_pattern(const std::string &pa
|
|||||||
{
|
{
|
||||||
std::string normalized;
|
std::string normalized;
|
||||||
normalized.reserve(pattern.size());
|
normalized.reserve(pattern.size());
|
||||||
|
bool current_group_has_steps = false;
|
||||||
for (char c : pattern) {
|
for (char c : pattern) {
|
||||||
char step = '\0';
|
char step = '\0';
|
||||||
if (decode_pattern_step(c, step)) {
|
if (decode_pattern_step(c, step)) {
|
||||||
normalized.push_back(step);
|
normalized.push_back(step);
|
||||||
|
current_group_has_steps = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == ',') {
|
||||||
|
if (!current_group_has_steps)
|
||||||
|
return std::string();
|
||||||
|
normalized.push_back(',');
|
||||||
|
current_group_has_steps = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (is_pattern_separator(c))
|
if (is_pattern_separator(c))
|
||||||
@@ -870,9 +948,16 @@ std::string MixedFilamentManager::normalize_manual_pattern(const std::string &pa
|
|||||||
// Unknown token => invalid pattern.
|
// Unknown token => invalid pattern.
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
if (!normalized.empty() && normalized.back() == ',')
|
||||||
|
return std::string();
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MixedFilamentManager::mix_percent_from_manual_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
return mix_percent_from_normalized_pattern(normalize_manual_pattern(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
||||||
float lower_bound,
|
float lower_bound,
|
||||||
float upper_bound,
|
float upper_bound,
|
||||||
@@ -1119,16 +1204,12 @@ unsigned int MixedFilamentManager::resolve(unsigned int filament_id,
|
|||||||
// steps: '1' => component_a, '2' => component_b, '3'..'9' => direct
|
// steps: '1' => component_a, '2' => component_b, '3'..'9' => direct
|
||||||
// physical filament IDs.
|
// physical filament IDs.
|
||||||
if (!mf.manual_pattern.empty()) {
|
if (!mf.manual_pattern.empty()) {
|
||||||
const int pos = safe_mod(layer_index, int(mf.manual_pattern.size()));
|
const std::string flattened_pattern = flatten_manual_pattern_groups(mf.manual_pattern);
|
||||||
const char token = mf.manual_pattern[size_t(pos)];
|
if (!flattened_pattern.empty()) {
|
||||||
if (token == '2')
|
const int pos = safe_mod(layer_index, int(flattened_pattern.size()));
|
||||||
return mf.component_b;
|
const unsigned int resolved = physical_filament_from_pattern_step(flattened_pattern[size_t(pos)], mf, num_physical);
|
||||||
if (token == '1')
|
if (resolved >= 1 && resolved <= num_physical)
|
||||||
return mf.component_a;
|
return resolved;
|
||||||
if (token >= '3' && token <= '9') {
|
|
||||||
const unsigned int direct = unsigned(token - '0');
|
|
||||||
if (direct >= 1 && direct <= num_physical)
|
|
||||||
return direct;
|
|
||||||
}
|
}
|
||||||
return mf.component_a;
|
return mf.component_a;
|
||||||
}
|
}
|
||||||
@@ -1174,6 +1255,78 @@ unsigned int MixedFilamentManager::resolve(unsigned int filament_id,
|
|||||||
return (pos < mf.ratio_a) ? mf.component_a : mf.component_b;
|
return (pos < mf.ratio_a) ? mf.component_a : mf.component_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int MixedFilamentManager::resolve_perimeter(unsigned int filament_id,
|
||||||
|
size_t num_physical,
|
||||||
|
int layer_index,
|
||||||
|
int perimeter_index,
|
||||||
|
float layer_print_z,
|
||||||
|
float layer_height,
|
||||||
|
bool force_height_weighted) const
|
||||||
|
{
|
||||||
|
const int mixed_idx = mixed_index_from_filament_id(filament_id, num_physical);
|
||||||
|
if (mixed_idx < 0)
|
||||||
|
return filament_id;
|
||||||
|
|
||||||
|
const MixedFilament &mf = m_mixed[size_t(mixed_idx)];
|
||||||
|
if (!mf.manual_pattern.empty()) {
|
||||||
|
const std::vector<std::string> pattern_groups = split_manual_pattern_groups(mf.manual_pattern);
|
||||||
|
if (!pattern_groups.empty()) {
|
||||||
|
const size_t group_idx = size_t(std::max(0, perimeter_index));
|
||||||
|
const std::string &group = pattern_groups[std::min(group_idx, pattern_groups.size() - 1)];
|
||||||
|
if (!group.empty()) {
|
||||||
|
const int pos = safe_mod(layer_index, int(group.size()));
|
||||||
|
const unsigned int resolved = physical_filament_from_pattern_step(group[size_t(pos)], mf, num_physical);
|
||||||
|
if (resolved >= 1 && resolved <= num_physical)
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(filament_id, num_physical, layer_index, layer_print_z, layer_height, force_height_weighted);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned int> MixedFilamentManager::ordered_perimeter_extruders(unsigned int filament_id,
|
||||||
|
size_t num_physical,
|
||||||
|
int layer_index,
|
||||||
|
float layer_print_z,
|
||||||
|
float layer_height,
|
||||||
|
bool force_height_weighted) const
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> ordered;
|
||||||
|
|
||||||
|
const int mixed_idx = mixed_index_from_filament_id(filament_id, num_physical);
|
||||||
|
if (mixed_idx < 0) {
|
||||||
|
ordered.emplace_back(filament_id);
|
||||||
|
return ordered;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MixedFilament &mf = m_mixed[size_t(mixed_idx)];
|
||||||
|
if (!mf.manual_pattern.empty()) {
|
||||||
|
const std::vector<std::string> pattern_groups = split_manual_pattern_groups(mf.manual_pattern);
|
||||||
|
if (!pattern_groups.empty()) {
|
||||||
|
ordered.reserve(pattern_groups.size());
|
||||||
|
for (size_t group_idx = 0; group_idx < pattern_groups.size(); ++group_idx) {
|
||||||
|
const unsigned int resolved = resolve_perimeter(filament_id,
|
||||||
|
num_physical,
|
||||||
|
layer_index,
|
||||||
|
int(group_idx),
|
||||||
|
layer_print_z,
|
||||||
|
layer_height,
|
||||||
|
force_height_weighted);
|
||||||
|
if (resolved < 1 || resolved > num_physical)
|
||||||
|
continue;
|
||||||
|
if (std::find(ordered.begin(), ordered.end(), resolved) == ordered.end())
|
||||||
|
ordered.emplace_back(resolved);
|
||||||
|
}
|
||||||
|
if (!ordered.empty())
|
||||||
|
return ordered;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ordered.emplace_back(resolve(filament_id, num_physical, layer_index, layer_print_z, layer_height, force_height_weighted));
|
||||||
|
return ordered;
|
||||||
|
}
|
||||||
|
|
||||||
int MixedFilamentManager::mixed_index_from_filament_id(unsigned int filament_id, size_t num_physical) const
|
int MixedFilamentManager::mixed_index_from_filament_id(unsigned int filament_id, size_t num_physical) const
|
||||||
{
|
{
|
||||||
if (filament_id <= num_physical)
|
if (filament_id <= num_physical)
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ public:
|
|||||||
// Normalize a manual mixed-pattern string into compact token form.
|
// Normalize a manual mixed-pattern string into compact token form.
|
||||||
// Accepts separators and A/B aliases. Returns empty string if invalid.
|
// Accepts separators and A/B aliases. Returns empty string if invalid.
|
||||||
static std::string normalize_manual_pattern(const std::string &pattern);
|
static std::string normalize_manual_pattern(const std::string &pattern);
|
||||||
|
static int mix_percent_from_manual_pattern(const std::string &pattern);
|
||||||
|
|
||||||
// ---- Queries --------------------------------------------------------
|
// ---- Queries --------------------------------------------------------
|
||||||
|
|
||||||
@@ -164,6 +165,19 @@ public:
|
|||||||
float layer_print_z = 0.f,
|
float layer_print_z = 0.f,
|
||||||
float layer_height = 0.f,
|
float layer_height = 0.f,
|
||||||
bool force_height_weighted = false) const;
|
bool force_height_weighted = false) const;
|
||||||
|
unsigned int resolve_perimeter(unsigned int filament_id,
|
||||||
|
size_t num_physical,
|
||||||
|
int layer_index,
|
||||||
|
int perimeter_index,
|
||||||
|
float layer_print_z = 0.f,
|
||||||
|
float layer_height = 0.f,
|
||||||
|
bool force_height_weighted = false) const;
|
||||||
|
std::vector<unsigned int> ordered_perimeter_extruders(unsigned int filament_id,
|
||||||
|
size_t num_physical,
|
||||||
|
int layer_index,
|
||||||
|
float layer_print_z = 0.f,
|
||||||
|
float layer_height = 0.f,
|
||||||
|
bool force_height_weighted = false) const;
|
||||||
|
|
||||||
// Map virtual filament ID (1-based, after physical IDs) to index into
|
// Map virtual filament ID (1-based, after physical IDs) to index into
|
||||||
// m_mixed. Virtual IDs enumerate enabled mixed rows only.
|
// m_mixed. Virtual IDs enumerate enabled mixed rows only.
|
||||||
|
|||||||
@@ -3467,7 +3467,8 @@ void MixedFilamentConfigPanel::build_ui()
|
|||||||
wxSize(FromDIP(200), -1), wxTE_PROCESS_ENTER);
|
wxSize(FromDIP(200), -1), wxTE_PROCESS_ENTER);
|
||||||
m_pattern_ctrl->SetToolTip(_L("Manual repeating pattern. Use 1/2 or A/B for component A/B, "
|
m_pattern_ctrl->SetToolTip(_L("Manual repeating pattern. Use 1/2 or A/B for component A/B, "
|
||||||
"and 3..9 for direct physical filament IDs. "
|
"and 3..9 for direct physical filament IDs. "
|
||||||
"Example: 1/1/1/1/2/2/2/2 or 1/2/3/4."));
|
"Use commas to define deeper perimeter patterns, for example 12,21. "
|
||||||
|
"Example: 1/1/1/1/2/2/2/2, 12,21, or 1/2/3/4."));
|
||||||
pattern_row->Add(m_pattern_ctrl, 1, wxALIGN_CENTER_VERTICAL);
|
pattern_row->Add(m_pattern_ctrl, 1, wxALIGN_CENTER_VERTICAL);
|
||||||
root->Add(pattern_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
|
root->Add(pattern_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
|
||||||
|
|
||||||
@@ -3581,8 +3582,7 @@ void MixedFilamentConfigPanel::build_ui()
|
|||||||
if (into_u8(m_pattern_ctrl->GetValue()) != normalized)
|
if (into_u8(m_pattern_ctrl->GetValue()) != normalized)
|
||||||
m_pattern_ctrl->ChangeValue(from_u8(normalized));
|
m_pattern_ctrl->ChangeValue(from_u8(normalized));
|
||||||
m_mf.manual_pattern = normalized;
|
m_mf.manual_pattern = normalized;
|
||||||
const int count_b = int(std::count(normalized.begin(), normalized.end(), '2'));
|
m_mf.mix_b_percent = MixedFilamentManager::mix_percent_from_manual_pattern(normalized);
|
||||||
m_mf.mix_b_percent = std::clamp((100 * count_b + int(normalized.size()) / 2) / std::max(1, int(normalized.size())), 0, 100);
|
|
||||||
m_mf.pointillism_all_filaments = false;
|
m_mf.pointillism_all_filaments = false;
|
||||||
m_mf.gradient_component_ids.clear();
|
m_mf.gradient_component_ids.clear();
|
||||||
m_mf.gradient_component_weights.clear();
|
m_mf.gradient_component_weights.clear();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/ExtrusionEntity.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -123,3 +124,71 @@ TEST_CASE("Mixed filament remap keeps later painted colors stable when an earlie
|
|||||||
CHECK(remap[7] == virtual_id_for_stable_id(mixed, 4, stable_id_7));
|
CHECK(remap[7] == virtual_id_for_stable_id(mixed, 4, stable_id_7));
|
||||||
CHECK(remap[8] == virtual_id_for_stable_id(mixed, 4, stable_id_8));
|
CHECK(remap[8] == virtual_id_for_stable_id(mixed, 4, stable_id_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Mixed filament grouped manual patterns normalize and round-trip", "[MixedFilament]")
|
||||||
|
{
|
||||||
|
const std::vector<std::string> colors = {"#FF0000", "#0000FF"};
|
||||||
|
|
||||||
|
MixedFilamentManager mgr;
|
||||||
|
mgr.add_custom_filament(1, 2, 50, colors);
|
||||||
|
REQUIRE(mgr.mixed_filaments().size() == 1);
|
||||||
|
|
||||||
|
MixedFilament &row = mgr.mixed_filaments().front();
|
||||||
|
row.manual_pattern = MixedFilamentManager::normalize_manual_pattern("1/1/1/1/1/1/1/2, 1/1/1/2/1/1/1/1");
|
||||||
|
REQUIRE(row.manual_pattern == "11111112,11121111");
|
||||||
|
|
||||||
|
const std::string serialized = mgr.serialize_custom_entries();
|
||||||
|
|
||||||
|
MixedFilamentManager loaded;
|
||||||
|
loaded.load_custom_entries(serialized, colors);
|
||||||
|
REQUIRE(loaded.mixed_filaments().size() == 1);
|
||||||
|
CHECK(loaded.mixed_filaments().front().manual_pattern == "11111112,11121111");
|
||||||
|
CHECK(loaded.mixed_filaments().front().mix_b_percent == 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Mixed filament perimeter resolver uses grouped manual patterns by inset", "[MixedFilament]")
|
||||||
|
{
|
||||||
|
const std::vector<std::string> colors = {"#00FFFF", "#FF00FF"};
|
||||||
|
|
||||||
|
MixedFilamentManager mgr;
|
||||||
|
mgr.add_custom_filament(1, 2, 50, colors);
|
||||||
|
REQUIRE(mgr.mixed_filaments().size() == 1);
|
||||||
|
|
||||||
|
MixedFilament &row = mgr.mixed_filaments().front();
|
||||||
|
row.manual_pattern = MixedFilamentManager::normalize_manual_pattern("12,21");
|
||||||
|
REQUIRE(row.manual_pattern == "12,21");
|
||||||
|
|
||||||
|
const unsigned int mixed_filament_id = 3;
|
||||||
|
CHECK(mgr.resolve(mixed_filament_id, 2, 0) == 1);
|
||||||
|
CHECK(mgr.resolve(mixed_filament_id, 2, 1) == 2);
|
||||||
|
|
||||||
|
CHECK(mgr.resolve_perimeter(mixed_filament_id, 2, 0, 0) == 1);
|
||||||
|
CHECK(mgr.resolve_perimeter(mixed_filament_id, 2, 1, 0) == 2);
|
||||||
|
CHECK(mgr.resolve_perimeter(mixed_filament_id, 2, 0, 1) == 2);
|
||||||
|
CHECK(mgr.resolve_perimeter(mixed_filament_id, 2, 1, 1) == 1);
|
||||||
|
CHECK(mgr.resolve_perimeter(mixed_filament_id, 2, 0, 3) == 2);
|
||||||
|
CHECK(mgr.resolve_perimeter(mixed_filament_id, 2, 1, 3) == 1);
|
||||||
|
|
||||||
|
const std::vector<unsigned int> ordered_layer0 = mgr.ordered_perimeter_extruders(mixed_filament_id, 2, 0);
|
||||||
|
const std::vector<unsigned int> ordered_layer1 = mgr.ordered_perimeter_extruders(mixed_filament_id, 2, 1);
|
||||||
|
REQUIRE(ordered_layer0.size() == 2);
|
||||||
|
REQUIRE(ordered_layer1.size() == 2);
|
||||||
|
CHECK(ordered_layer0[0] == 1);
|
||||||
|
CHECK(ordered_layer0[1] == 2);
|
||||||
|
CHECK(ordered_layer1[0] == 2);
|
||||||
|
CHECK(ordered_layer1[1] == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ExtrusionPath copies preserve inset index", "[MixedFilament]")
|
||||||
|
{
|
||||||
|
ExtrusionPath src(erPerimeter);
|
||||||
|
src.inset_idx = 3;
|
||||||
|
|
||||||
|
ExtrusionPath copied(src);
|
||||||
|
CHECK(copied.inset_idx == 3);
|
||||||
|
|
||||||
|
ExtrusionPath assigned(erExternalPerimeter);
|
||||||
|
assigned.inset_idx = 0;
|
||||||
|
assigned = src;
|
||||||
|
CHECK(assigned.inset_idx == 3);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user