bug fix, tests

This commit is contained in:
Rad
2026-04-09 21:25:33 +02:00
parent ed0182618e
commit a11b70e3a3
15 changed files with 1014 additions and 121 deletions
+27 -8
View File
@@ -904,8 +904,33 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
if (optdef && optdef->type == coStrings) {
use_comma = false;
}
std::vector<std::string> array_values;
array_values.reserve(it.value().size());
for (auto iter = it.value().begin(); iter != it.value().end(); iter++) {
if (iter.value().is_string()) {
array_values.emplace_back(iter.value());
}
else {
//should not happen
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<file<<" error, invalid json array for " << it.key();
valid = false;
break;
}
}
if (valid && optdef != nullptr && optdef->is_scalar() && optdef->type != coPoint && optdef->type != coPoint3) {
if (array_values.size() == 1) {
value_str = array_values.front();
} else if (!array_values.empty() &&
std::all_of(array_values.begin() + 1, array_values.end(),
[&](const std::string &value) { return value == array_values.front(); })) {
value_str = array_values.front();
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__
<< ": collapsing redundant json array for scalar option " << it.key()
<< " in " << file;
}
}
if (valid && value_str.empty()) {
for (const std::string &array_value : array_values) {
if (!first) {
if (use_comma)
value_str += ",";
@@ -916,19 +941,13 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
first = false;
if (use_comma)
value_str += iter.value();
value_str += array_value;
else {
value_str += "\"";
value_str += escape_string_cstyle(iter.value());
value_str += escape_string_cstyle(array_value);
value_str += "\"";
}
}
else {
//should not happen
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<file<<" error, invalid json array for " << it.key();
valid = false;
break;
}
}
if (valid)
this->set_deserialize(opt_key, value_str, substitution_context);
+5 -4
View File
@@ -842,7 +842,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
const PrintRegionConfig &region_config = layerm.region().config();
FlowRole extrusion_role = surface.is_top() ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill);
bool is_bridge = layer.id() > 0 && surface.is_bridge();
params.extruder = layerm.region().extruder(extrusion_role);
const unsigned int effective_extruder = layerm.extruder(extrusion_role);
params.extruder = effective_extruder;
params.pattern = region_config.sparse_infill_pattern.value;
params.density = float(region_config.sparse_infill_density);
params.lateral_lattice_angle_1 = region_config.lateral_lattice_angle_1;
@@ -939,9 +940,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
} else {
// Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status,
// so that internall infill will be aligned over all layers of the current region.
params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing();
params.spacing = layerm.flow(frInfill, layer.object()->config().layer_height).spacing();
// Anchor a sparse infill to inner perimeters with the following anchor length:
params.anchor_length = float(region_config.infill_anchor);
params.anchor_length = float(region_config.infill_anchor);
if (region_config.infill_anchor.percent)
params.anchor_length = float(params.anchor_length * 0.01 * params.spacing);
params.anchor_length_max = float(region_config.infill_anchor_max);
@@ -953,7 +954,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
//get locked region param
if (params.pattern == ipLockedZag){
const PrintObject *object = layerm.layer()->object();
auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(layerm.region().extruder(extrusion_role) - 1));
auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(effective_extruder - 1));
Flow skin_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skin_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness));
//add skin flow
append_flow_param(lock_param.skin_flow_params, skin_flow, surface.expolygon);
+51 -10
View File
@@ -10,6 +10,46 @@
namespace Slic3r {
namespace {
SurfaceCollection split_fill_surfaces_by_region(const SurfaceCollection &fill_surfaces, const SurfaceCollection &region_slices)
{
SurfaceCollection out;
if (fill_surfaces.empty() || region_slices.empty())
return out;
for (size_t surface_type = 0; surface_type < size_t(stCount); ++surface_type) {
Surfaces typed_fill_surfaces;
for (const Surface &surface : fill_surfaces.surfaces)
if (surface.surface_type == SurfaceType(surface_type))
typed_fill_surfaces.emplace_back(surface);
if (typed_fill_surfaces.empty())
continue;
ExPolygons clipped = intersection_ex(typed_fill_surfaces, region_slices.surfaces, ApplySafetyOffset::Yes);
if (!clipped.empty())
out.append(std::move(clipped), SurfaceType(surface_type));
}
return out;
}
using SliceMergeKey = std::tuple<int, double, unsigned short, double, unsigned short>;
SliceMergeKey slice_merge_key(const Surface &surface)
{
return {
int(surface.surface_type),
surface.thickness,
surface.thickness_layers,
surface.bridge_angle,
surface.extra_perimeters
};
}
} // namespace
Layer::~Layer()
{
this->lower_layer = this->upper_layer = nullptr;
@@ -178,6 +218,7 @@ bool Layer::is_perimeter_compatible(const PrintRegion& a, const PrintRegion& b)
void Layer::make_perimeters()
{
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id();
const bool disable_compatible_region_merge = this->object() != nullptr && this->object()->is_mm_painted();
// keep track of regions whose perimeters we have already generated
std::vector<unsigned char> done(m_regions.size(), false);
@@ -198,7 +239,7 @@ void Layer::make_perimeters()
// find compatible regions
LayerRegionPtrs layerms;
layerms.push_back(*layerm);
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
for (LayerRegionPtrs::const_iterator it = layerm + 1; !disable_compatible_region_merge && it != m_regions.end(); ++it)
if (! (*it)->slices.empty()) {
LayerRegion* other_layerm = *it;
const PrintRegion &other_region = other_layerm->region();
@@ -221,17 +262,17 @@ void Layer::make_perimeters()
// Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence.
LayerRegion *layerm_config = layerms.front();
{
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ]
// Keep surface typing intact when compatible regions are merged for perimeter generation.
std::map<SliceMergeKey, Surfaces> slices;
for (LayerRegion *layerm : layerms) {
for (const Surface &surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface);
slices[slice_merge_key(surface)].emplace_back(surface);
if (layerm->region().config().sparse_infill_density > layerm_config->region().config().sparse_infill_density)
layerm_config = layerm;
}
// merge the surfaces assigned to each group
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front());
for (auto &entry : slices)
new_slices.append(offset_ex(entry.second, ClipperSafetyOffset), entry.second.front());
}
// make perimeters
@@ -244,11 +285,11 @@ void Layer::make_perimeters()
if (!fill_surfaces.surfaces.empty()) {
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
// Separate the fill surfaces.
ExPolygons expp = intersection_ex(fill_surfaces.surfaces, (*l)->slices.surfaces);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
SurfaceCollection split_fill_surfaces = split_fill_surfaces_by_region(fill_surfaces, (*l)->slices);
(*l)->fill_expolygons = to_expolygons(split_fill_surfaces.surfaces);
(*l)->fill_surfaces = std::move(split_fill_surfaces);
//BBS: Separate fill_no_overlap
(*l)->fill_no_overlap_expolygons = intersection_ex((*l)->slices.surfaces, fill_no_overlap);
(*l)->fill_no_overlap_expolygons = intersection_ex((*l)->slices.surfaces, fill_no_overlap, ApplySafetyOffset::Yes);
}
}
}
+37 -5
View File
@@ -22,6 +22,30 @@ namespace Slic3r {
namespace {
unsigned int effective_layer_filament_id(const Layer &layer, unsigned int filament_id)
{
if (filament_id == 0)
return filament_id;
const PrintObject *object = layer.object();
const Print *print = object ? object->print() : nullptr;
if (print == nullptr)
return filament_id;
const size_t num_physical = print->config().filament_diameter.size();
if (num_physical == 0)
return filament_id;
// Ordinary mixed rows still print with one physical filament per layer even
// when region collapse is disabled, so geometry / flow decisions must use
// that effective physical filament to avoid per-layer thin-feature drift.
return print->mixed_filament_manager().effective_painted_region_filament_id(filament_id,
num_physical,
int(layer.id()),
float(layer.print_z),
float(layer.height));
}
bool use_base_infill_filament(const PrintRegionConfig &config, int layer_index, int layer_count)
{
if (!config.enable_infill_filament_override.value)
@@ -39,11 +63,15 @@ bool use_base_infill_filament(const PrintRegionConfig &config, int layer_index,
unsigned int LayerRegion::extruder(FlowRole role) const
{
const PrintRegionConfig &config = this->region().config();
unsigned int filament_id = 0;
if (role == frInfill)
return use_base_infill_filament(config, m_layer->id(), int(m_layer->object()->layers().size())) ? config.wall_filament.value : config.sparse_infill_filament.value;
if (role == frSolidInfill && std::abs(config.sparse_infill_density.value - 100.) < EPSILON)
return use_base_infill_filament(config, m_layer->id(), int(m_layer->object()->layers().size())) ? config.wall_filament.value : config.sparse_infill_filament.value;
return this->region().extruder(role);
filament_id = use_base_infill_filament(config, m_layer->id(), int(m_layer->object()->layers().size())) ? config.wall_filament.value : config.sparse_infill_filament.value;
else if (role == frSolidInfill && std::abs(config.sparse_infill_density.value - 100.) < EPSILON)
filament_id = use_base_infill_filament(config, m_layer->id(), int(m_layer->object()->layers().size())) ? config.wall_filament.value : config.sparse_infill_filament.value;
else
filament_id = this->region().extruder(role);
return effective_layer_filament_id(*m_layer, filament_id);
}
Flow LayerRegion::flow(FlowRole role) const
@@ -127,6 +155,10 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
const PrintConfig &print_config = this->layer()->object()->print()->config();
const PrintRegionConfig &region_config = this->region().config();
const PrintObjectConfig& object_config = this->layer()->object()->config();
PrintRegionConfig perimeter_config = region_config;
perimeter_config.wall_filament.value = int(effective_layer_filament_id(*this->layer(), unsigned(std::max(0, region_config.wall_filament.value))));
perimeter_config.sparse_infill_filament.value = int(effective_layer_filament_id(*this->layer(), unsigned(std::max(0, region_config.sparse_infill_filament.value))));
perimeter_config.solid_infill_filament.value = int(effective_layer_filament_id(*this->layer(), unsigned(std::max(0, region_config.solid_infill_filament.value))));
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
bool spiral_mode = print_config.spiral_mode &&
//FIXME account for raft layers.
@@ -140,7 +172,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
this->layer()->height,
this->layer()->slice_z,
this->flow(frPerimeter),
&region_config,
&perimeter_config,
&this->layer()->object()->config(),
&print_config,
spiral_mode,
+6 -7
View File
@@ -1830,15 +1830,14 @@ static std::vector<std::vector<ExPolygons>> merge_segmented_layers(const std::ve
{
const size_t num_layers = segmented_regions.size();
std::vector<std::vector<ExPolygons>> segmented_regions_merged(num_layers);
segmented_regions_merged.assign(num_layers, std::vector<ExPolygons>(num_facets_states - 1));
segmented_regions_merged.assign(num_layers, std::vector<ExPolygons>(num_facets_states));
assert(!top_and_bottom_layers.size() || num_facets_states == top_and_bottom_layers.size());
BOOST_LOG_TRIVIAL(debug) << "Print object segmentation - Merging segmented layers in parallel - Begin";
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&segmented_regions, &top_and_bottom_layers, &segmented_regions_merged, &num_facets_states, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
assert(segmented_regions[layer_idx].size() == num_facets_states);
// Zero is skipped because it is the default color of the volume
for (size_t extruder_id = 1; extruder_id < num_facets_states; ++extruder_id) {
for (size_t extruder_id = 0; extruder_id < num_facets_states; ++extruder_id) {
throw_on_cancel_callback();
if (!segmented_regions[layer_idx][extruder_id].empty()) {
ExPolygons segmented_regions_trimmed = segmented_regions[layer_idx][extruder_id];
@@ -1850,16 +1849,16 @@ static std::vector<std::vector<ExPolygons>> merge_segmented_layers(const std::ve
}
}
segmented_regions_merged[layer_idx][extruder_id - 1] = std::move(segmented_regions_trimmed);
segmented_regions_merged[layer_idx][extruder_id] = std::move(segmented_regions_trimmed);
}
if (!top_and_bottom_layers.empty() && !top_and_bottom_layers[extruder_id][layer_idx].empty()) {
bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id - 1].empty();
append(segmented_regions_merged[layer_idx][extruder_id - 1], top_and_bottom_layers[extruder_id][layer_idx]);
bool was_top_and_bottom_empty = segmented_regions_merged[layer_idx][extruder_id].empty();
append(segmented_regions_merged[layer_idx][extruder_id], top_and_bottom_layers[extruder_id][layer_idx]);
// Remove dimples (#7235) appearing after merging side segmentation of the model with tops and bottoms painted layers.
if (!was_top_and_bottom_empty)
segmented_regions_merged[layer_idx][extruder_id - 1] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id - 1]), float(SCALED_EPSILON), -float(SCALED_EPSILON));
segmented_regions_merged[layer_idx][extruder_id] = offset2_ex(union_ex(segmented_regions_merged[layer_idx][extruder_id]), float(SCALED_EPSILON), -float(SCALED_EPSILON));
}
}
}
+89
View File
@@ -30,6 +30,67 @@ namespace Slic3r {
using namespace Slic3r::Feature::FuzzySkin;
static void append_collapsed_infill_gap_fill(const ExPolygons &areas,
const Flow &flow,
const double simplify_resolution,
const float filter_out_gap_fill,
ExtrusionEntityCollection *gap_fill)
{
if (gap_fill == nullptr || areas.empty())
return;
const double min = 0.2 * flow.scaled_spacing() * (1 - INSET_OVERLAP_TOLERANCE);
const double max = 2.0 * flow.scaled_spacing();
ExPolygons gaps_ex = diff_ex(
opening_ex(areas, float(min / 2.0)),
offset2_ex(areas, -float(max / 2.0), float(max / 2.0 + ClipperSafetyOffset)));
if (gaps_ex.empty())
gaps_ex = union_ex(areas);
ThickPolylines polylines;
for (ExPolygon &ex : gaps_ex) {
ex.douglas_peucker(simplify_resolution);
try {
ex.medial_axis(min, max, &polylines);
} catch (...) {
}
}
auto filter_short_polylines = [&](ThickPolylines &src) {
src.erase(std::remove_if(src.begin(),
src.end(),
[&](const ThickPolyline &p) { return p.length() < scale_(filter_out_gap_fill); }),
src.end());
};
filter_short_polylines(polylines);
if (polylines.empty()) {
for (const double inset_factor : {0.25, 0.125}) {
ExPolygons inset_loops = offset_ex(areas, -float(flow.scaled_width() * inset_factor));
if (inset_loops.empty())
continue;
ThickPolylines inset_polylines = to_thick_polylines(to_polylines(std::move(inset_loops)), flow.scaled_width());
filter_short_polylines(inset_polylines);
if (inset_polylines.empty())
continue;
ExtrusionEntityCollection gap_fill_entities;
variable_width(inset_polylines, erGapFill, flow, gap_fill_entities.entities);
gap_fill->append(std::move(gap_fill_entities.entities));
return;
}
}
if (polylines.empty())
return;
ExtrusionEntityCollection gap_fill_entities;
variable_width(polylines, erGapFill, flow, gap_fill_entities.entities);
gap_fill->append(std::move(gap_fill_entities.entities));
}
// Hierarchy of perimeters.
class PerimeterGeneratorLoop {
public:
@@ -1208,6 +1269,7 @@ void PerimeterGenerator::process_classic()
ExPolygons gaps;
ExPolygons top_fills;
ExPolygons fill_clip;
ExPolygons collapsed_gap_fill_source;
if (loop_number >= 0) {
// In case no perimeters are to be generated, loop_number will equal to -1.
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
@@ -1536,6 +1598,9 @@ void PerimeterGenerator::process_classic()
}
}
}
collapsed_gap_fill_source = entities.empty() ?
ExPolygons() :
diff_ex(ExPolygons { surface.expolygon }, entities.polygons_covered_by_width(10.f));
// append perimeters for this slice as a collection
if (! entities.empty())
@@ -1634,6 +1699,17 @@ void PerimeterGenerator::process_classic()
not_filled_exp,
float(-inset - min_perimeter_infill_spacing / 2.),
float(min_perimeter_infill_spacing / 2.));
if (infill_exp.empty() && has_gap_fill) {
ExPolygons gap_fill_source = not_filled_exp.empty() ? collapsed_gap_fill_source : not_filled_exp;
if (gap_fill_source.empty())
gap_fill_source = ExPolygons { surface.expolygon };
append_collapsed_infill_gap_fill(gap_fill_source,
this->solid_infill_flow,
surface_simplify_resolution,
config->filter_out_gap_fill.value,
this->gap_fill);
}
// append infill areas to fill_surfaces
//if any top_fills, grow them by ext_perimeter_spacing/2 to have the real un-anchored fill
ExPolygons top_infill_exp = intersection_ex(fill_clip, offset_ex(top_fills, double(ext_perimeter_spacing / 2)));
@@ -2450,6 +2526,7 @@ void PerimeterGenerator::process_arachne()
steep_overhang_contour = true;
steep_overhang_hole = true;
}
ExPolygons collapsed_gap_fill_source;
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) {
// All walls are counter-clockwise initially, so we don't need to reorient it if that's what we want
if (wall_direction != WallDirection::CounterClockwise) {
@@ -2457,6 +2534,7 @@ void PerimeterGenerator::process_arachne()
// Reverse internal only if the wall direction is auto
this->config->overhang_reverse_internal_only && wall_direction == WallDirection::Auto);
}
collapsed_gap_fill_source = diff_ex(ExPolygons { surface.expolygon }, extrusion_coll.polygons_covered_by_width(10.f));
this->loops->append(extrusion_coll);
}
@@ -2496,6 +2574,17 @@ void PerimeterGenerator::process_arachne()
not_filled_exp,
float(-min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.));
if (infill_exp.empty() && this->config->gap_infill_speed.value > 0) {
ExPolygons gap_fill_source = not_filled_exp.empty() ? collapsed_gap_fill_source : not_filled_exp;
if (gap_fill_source.empty())
gap_fill_source = ExPolygons { surface.expolygon };
append_collapsed_infill_gap_fill(gap_fill_source,
this->solid_infill_flow,
surface_simplify_resolution,
config->filter_out_gap_fill.value,
this->gap_fill);
}
// append infill areas to fill_surfaces
if (!top_expolygons.empty()) {
infill_exp = union_ex(infill_exp, offset_ex(top_expolygons, double(top_inset)));
+23 -7
View File
@@ -1165,12 +1165,6 @@ void PresetCollection::load_presets(
if (Slic3r::is_json_file(file_name)) {
// Remove the .ini suffix.
std::string name = file_name.erase(file_name.size() - 5);
if (this->find_preset(name, false)) {
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue;
}
try {
Preset preset(m_type, name, false);
preset.file = dir_entry.path().string();
@@ -1201,6 +1195,28 @@ void PresetCollection::load_presets(
continue;
}
auto type_it = key_values.find(BBL_JSON_KEY_TYPE);
if (type_it != key_values.end()) {
Preset::Type file_type = Preset::get_type_from_string(type_it->second);
if (file_type != m_type) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(
": skip preset %1% because file type %2% does not match collection type %3%")
% preset.file % type_it->second % Preset::get_type_string(m_type);
continue;
}
}
auto name_it = key_values.find(BBL_JSON_KEY_NAME);
if (name_it != key_values.end() && !name_it->second.empty())
preset.name = name_it->second;
if (this->find_preset(preset.name, false)) {
// This happens when there is a preset with the same logical name already loaded,
// typically from a system bundle or from another JSON file with a different filename.
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << preset.name;
continue;
}
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> version = Semver::parse(version_str);
if (!version) continue;
@@ -1245,7 +1261,7 @@ void PresetCollection::load_presets(
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
preset.config = default_preset.config;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " load preset: " << name << " and filament_id: " << preset.filament_id << " and base_id: " << preset.base_id;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " load preset: " << preset.name << " and filament_id: " << preset.filament_id << " and base_id: " << preset.base_id;
preset.config.apply(std::move(config));
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
+111 -7
View File
@@ -1752,6 +1752,27 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
this->load_installed_filaments(config);
this->load_installed_sla_materials(config);
auto is_default_printer_selection = [](const std::string &name) {
return name.empty() || name == "Default Printer";
};
auto is_placeholder_print_selection = [](const std::string &name) {
return name.empty() || name == "Default Setting" || boost::algorithm::iends_with(name, "_common");
};
auto is_placeholder_filament_selection = [](const std::string &name) {
return name.empty() || name == "Default Filament";
};
auto unique_visible_printer = [this]() -> const Preset* {
const Preset *visible_printer = nullptr;
for (const Preset &preset : printers) {
if (preset.is_default || !preset.is_visible)
continue;
if (visible_printer != nullptr)
return nullptr;
visible_printer = &preset;
}
return visible_printer;
};
// Parse the initial print / filament / printer profile names.
// std::string initial_sla_print_profile_name = remove_ini_suffix(config.get("presets", PRESET_SLA_PRINT_NAME));
// std::string initial_sla_material_profile_name = remove_ini_suffix(config.get("presets", PRESET_SLA_MATERIALS_NAME));
@@ -1766,7 +1787,10 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
const Preset *initial_printer = printers.find_preset(initial_printer_profile_name);
// If executed due to a Config Wizard update, preferred_printer contains the first newly installed printer, otherwise nullptr.
const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant);
printers.select_preset_by_name(preferred_printer ? preferred_printer->name : initial_printer_profile_name, true);
const Preset *fallback_printer = preferred_printer == nullptr && is_default_printer_selection(initial_printer_profile_name) ?
unique_visible_printer() : nullptr;
const Preset *selected_printer = preferred_printer ? preferred_printer : fallback_printer ? fallback_printer : initial_printer;
printers.select_preset_by_name(selected_printer ? selected_printer->name : initial_printer_profile_name, true);
CNumericLocalesSetter locales_setter;
// Orca: load from orca_presets
@@ -1774,16 +1798,21 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
std::string initial_print_profile_name = config.get_printer_setting(initial_printer_profile_name, PRESET_PRINT_NAME);
std::string initial_filament_profile_name = config.get_printer_setting(initial_printer_profile_name, PRESET_FILAMENT_NAME);
//BBS: set default print/filament profiles to BBL's default setting
if (preferred_printer)
// Normalize placeholder selections to the active printer defaults.
if (selected_printer)
{
const std::string& prefered_print_profile = preferred_printer->config.opt_string("default_print_profile");
if ((!initial_print_profile_name.compare("Default Setting")) && (prefered_print_profile.size() > 0))
const std::string& prefered_print_profile = selected_printer->config.opt_string("default_print_profile");
if (is_placeholder_print_selection(initial_print_profile_name) && !prefered_print_profile.empty())
initial_print_profile_name = prefered_print_profile;
const std::vector<std::string>& prefered_filament_profiles = preferred_printer->config.option<ConfigOptionStrings>("default_filament_profile")->values;
if ((!initial_filament_profile_name.compare("Default Filament")) && (prefered_filament_profiles.size() > 0))
const std::vector<std::string>& prefered_filament_profiles = selected_printer->config.option<ConfigOptionStrings>("default_filament_profile")->values;
if (is_placeholder_filament_selection(initial_filament_profile_name) && !prefered_filament_profiles.empty())
initial_filament_profile_name = prefered_filament_profiles[0];
if (fallback_printer != nullptr) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": recovered active printer selection from %1% to %2%")
% initial_printer_profile_name % fallback_printer->name;
}
}
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
@@ -2851,6 +2880,46 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": invalid type for " << it.key();
}
};
auto append_json_subfiles = [](const fs::path& vendor_dir,
const fs::path& section_dir,
std::vector<std::pair<std::string, std::string>>& subfile_map,
bool recursive) {
if (!fs::exists(section_dir))
return;
std::vector<fs::path> files;
if (recursive) {
for (const auto& entry : fs::recursive_directory_iterator(section_dir)) {
if (fs::is_regular_file(entry.path()) && Slic3r::is_json_file(entry.path().filename().string()))
files.push_back(entry.path());
}
} else {
for (const auto& entry : fs::directory_iterator(section_dir)) {
if (fs::is_regular_file(entry.path()) && Slic3r::is_json_file(entry.path().filename().string()))
files.push_back(entry.path());
}
}
auto priority = [](const fs::path& file) {
const std::string stem = file.stem().string();
if (stem == "fdm_filament_common")
return 0;
if (boost::algorithm::iends_with(stem, "@base"))
return 1;
if (boost::algorithm::iends_with(stem, "_common"))
return 2;
return 3;
};
std::sort(files.begin(), files.end(), [&](const fs::path& lhs, const fs::path& rhs) {
const int lhs_priority = priority(lhs);
const int rhs_priority = priority(rhs);
if (lhs_priority != rhs_priority)
return lhs_priority < rhs_priority;
return lhs.generic_string() < rhs.generic_string();
});
for (const fs::path& file : files)
subfile_map.emplace_back(file.stem().string(), file.lexically_relative(vendor_dir).generic_string());
};
try {
boost::nowide::ifstream ifs(root_file);
json j;
@@ -2905,6 +2974,29 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
//goto __error_process;
}
if (vendor_name == ORCA_FILAMENT_LIBRARY && filament_subfiles.empty()) {
const fs::path vendor_dir = fs::path(path) / vendor_name;
const fs::path filament_dir = vendor_dir / "filament";
// OrcaFilamentLibrary keeps its profiles on disk but ships an empty manifest. Load the shared
// base presets first, then the top-level generic presets, then the vendor-specific subfolders.
append_json_subfiles(vendor_dir, filament_dir / "base", filament_subfiles, true);
append_json_subfiles(vendor_dir, filament_dir, filament_subfiles, false);
if (fs::exists(filament_dir)) {
std::vector<fs::path> vendor_dirs;
for (const auto& entry : fs::directory_iterator(filament_dir)) {
if (fs::is_directory(entry.path()) && entry.path().filename() != "base")
vendor_dirs.push_back(entry.path());
}
std::sort(vendor_dirs.begin(), vendor_dirs.end());
for (const fs::path& dir_entry : vendor_dirs)
append_json_subfiles(vendor_dir, dir_entry, filament_subfiles, true);
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": discovered " << filament_subfiles.size()
<< " OrcaFilamentLibrary filament profiles from disk";
}
if (startup_profile) {
startup_profile_log("PresetBundle::load_vendor_configs_from_json vendor=" + vendor_name +
" machine_models=" + std::to_string(machine_model_subfiles.size()) +
@@ -3068,6 +3160,18 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
auto filament_it = key_values.find(BBL_JSON_KEY_FILAMENT_ID);
if (filament_it != key_values.end())
filament_id = filament_it->second;
if (filament_id.empty() &&
presets_collection->type() == Preset::TYPE_FILAMENT &&
flags.has(LoadConfigBundleAttribute::LoadSystem) &&
!setting_id.empty())
{
// Some system bundles only provide setting_id for filaments. Treat it as a stable fallback
// instead of aborting the entire vendor import and losing all dependent presets.
filament_id = setting_id;
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__
<< ": missing filament_id for " << preset_name
<< ", falling back to setting_id " << setting_id;
}
//check whether it inherits other preset or not
auto it1 = key_values.find(BBL_JSON_KEY_INHERITS);
if (it1 != key_values.end()) {
+31 -3
View File
@@ -709,6 +709,27 @@ PrintObjectRegions::BoundingBox find_modifier_volume_extents(const PrintObjectRe
return out;
}
static const ModelVolume *root_model_part_for_parent_region(const PrintObjectRegions::LayerRangeRegions &layer_range, int parent_region_id)
{
if (parent_region_id < 0 || parent_region_id >= int(layer_range.volume_regions.size()))
return nullptr;
const PrintObjectRegions::VolumeRegion *region = &layer_range.volume_regions[size_t(parent_region_id)];
while (region != nullptr && !region->model_volume->is_model_part()) {
if (region->parent < 0 || region->parent >= int(layer_range.volume_regions.size()))
return nullptr;
region = &layer_range.volume_regions[size_t(region->parent)];
}
return (region != nullptr && region->model_volume->is_model_part()) ? region->model_volume : nullptr;
}
static bool mm_paint_applies_to_parent_region(const PrintObjectRegions::LayerRangeRegions &layer_range, int parent_region_id)
{
const ModelVolume *root_model_part = root_model_part_for_parent_region(layer_range, parent_region_id);
return root_model_part != nullptr && root_model_part->is_mm_painted();
}
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; }
@@ -798,6 +819,8 @@ bool verify_update_print_object_regions(
// Verify and / or update PrintRegions produced by color painting.
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
for (const PrintObjectRegions::PaintedRegion &region : layer_range.painted_regions) {
if (!mm_paint_applies_to_parent_region(layer_range, region.parent))
return false;
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[region.parent];
PrintRegionConfig cfg = parent_region.region->config();
cfg.wall_filament.value = region.extruder_id;
@@ -993,7 +1016,11 @@ static PrintObjectRegions* generate_print_object_regions(
region_set.emplace(it, region);
return region;
};
auto create_unique_region = [&all_regions](PrintRegionConfig &&config) -> PrintRegion* {
size_t hash = config.hash();
all_regions.emplace_back(std::make_unique<PrintRegion>(std::move(config), hash, int(all_regions.size())));
return all_regions.back().get();
};
// Chain the regions in the order they are stored in the volumes list.
for (int volume_id = 0; volume_id < int(model_volumes.size()); ++ volume_id) {
const ModelVolume &volume = *model_volumes[volume_id];
@@ -1043,12 +1070,13 @@ static PrintObjectRegions* generate_print_object_regions(
for (unsigned int painted_extruder_id : painting_extruders)
for (int parent_region_id = 0; parent_region_id < int(layer_range.volume_regions.size()); ++ parent_region_id)
if (const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
(parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) &&
mm_paint_applies_to_parent_region(layer_range, parent_region_id)) {
PrintRegionConfig cfg = parent_region.region->config();
cfg.wall_filament.value = painted_extruder_id;
cfg.solid_infill_filament.value = painted_extruder_id;
cfg.sparse_infill_filament.value = painted_extruder_id;
PrintRegion *painted_region = get_create_region(std::move(cfg));
PrintRegion *painted_region = create_unique_region(std::move(cfg));
if (painted_region->config().wall_filament.value != painted_extruder_id ||
painted_region->config().solid_infill_filament.value != painted_extruder_id ||
painted_region->config().sparse_infill_filament.value != painted_extruder_id) {
+600 -45
View File
@@ -8,6 +8,7 @@
#include <iomanip>
#include <limits>
#include <numeric>
#include <sstream>
#include <tbb/parallel_for.h>
@@ -30,6 +31,10 @@ namespace Slic3r {
bool PrintObject::clip_multipart_objects = true;
bool PrintObject::infill_only_where_needed = false;
static bool has_surface_emboss_mixed_volume(const PrintObject &print_object);
static std::string surface_emboss_mixed_debug_file_path(const PrintObject &print_object);
static void reset_surface_emboss_mixed_debug_file(const PrintObject &print_object);
LayerPtrs new_layers(
PrintObject *print_object,
// Object layers (pairs of bottom/top Z coordinate), without the raft.
@@ -808,6 +813,12 @@ void PrintObject::slice()
m_typed_slices = false;
this->clear_layers();
m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile, m_config.precise_z_height.value));
if (has_surface_emboss_mixed_volume(*this)) {
reset_surface_emboss_mixed_debug_file(*this);
BOOST_LOG_TRIVIAL(warning) << "Surface emboss mixed debug enabled"
<< " object=" << (this->model_object() ? this->model_object()->name : std::string("<unknown>"))
<< " debug_file=" << surface_emboss_mixed_debug_file_path(*this);
}
this->slice_volumes();
m_print->throw_if_canceled();
int firstLayerReplacedBy = 0;
@@ -872,6 +883,13 @@ static coordf_t float_from_full_config(const DynamicPrintConfig &full_cfg, const
return coordf_t(full_cfg.opt_float(key));
}
static inline unsigned int segmentation_channel_filament_id(size_t channel_idx)
{
// MM segmentation reserves channel 0 for the parent/default region.
// All remaining channels already line up with the 1-based filament IDs.
return unsigned(channel_idx);
}
static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vector<std::vector<ExPolygons>> &segmentation)
{
const Print *print = print_object.print();
@@ -2290,11 +2308,37 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager();
const auto &mixed_rows = mixed_mgr.mixed_filaments();
size_t pointillism_rows = 0;
for (const MixedFilament &mf : mixed_rows) {
const std::vector<unsigned int> sequence = pointillism_sequence_for_row(mf, num_physical);
std::vector<uint8_t> pointillism_row_eligible(mixed_rows.size(), uint8_t(0));
for (size_t row_idx = 0; row_idx < mixed_rows.size(); ++row_idx) {
const std::vector<unsigned int> sequence = pointillism_sequence_for_row(mixed_rows[row_idx], num_physical);
if (unique_extruder_count(sequence, num_physical) >= 2)
++pointillism_rows;
pointillism_row_eligible[row_idx] = uint8_t(1);
}
size_t pointillism_rows = 0;
if (!pointillism_row_eligible.empty()) {
std::vector<uint8_t> pointillism_row_active(pointillism_row_eligible.size(), uint8_t(0));
for (size_t layer_id = 0; layer_id < segmentation.size(); ++layer_id) {
const auto &layer_segmentation = segmentation[layer_id];
for (size_t channel_idx = 1; channel_idx < layer_segmentation.size(); ++channel_idx) {
if (layer_segmentation[channel_idx].empty())
continue;
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
if (!mixed_mgr.is_mixed(state_id, num_physical))
continue;
const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical);
if (mixed_idx < 0 || size_t(mixed_idx) >= pointillism_row_eligible.size())
continue;
const size_t row_idx = size_t(mixed_idx);
if (pointillism_row_eligible[row_idx] == 0 || pointillism_row_active[row_idx] != 0)
continue;
pointillism_row_active[row_idx] = uint8_t(1);
++pointillism_rows;
}
}
}
if (pointillism_rows > 0) {
@@ -2373,7 +2417,7 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
const ExPolygons &state_masks = segmentation[layer_id][channel_idx];
if (state_masks.empty())
continue;
const unsigned int state_id = unsigned(channel_idx + 1);
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
if (!mixed_mgr.is_mixed(state_id, num_physical))
continue;
@@ -2443,7 +2487,7 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
const ExPolygons &state_masks = segmentation[layer_id][channel_idx];
if (state_masks.empty())
continue;
const unsigned int state_id = unsigned(channel_idx + 1);
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
if (!mixed_mgr.is_mixed(state_id, num_physical))
continue;
const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical);
@@ -2757,7 +2801,7 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
if (state_masks.empty())
continue;
const unsigned int state_id = unsigned(channel_idx + 1);
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
if (!mixed_mgr.is_mixed(state_id, num_physical))
continue;
const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical);
@@ -2846,7 +2890,7 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
if (state_masks.empty())
continue;
const unsigned int state_id = unsigned(channel_idx + 1);
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
if (!mixed_mgr.is_mixed(state_id, num_physical))
continue;
const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical);
@@ -2965,20 +3009,61 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
const auto &layer_ranges = print_object.shared_regions()->layer_ranges;
double z = print_object.get_layer(int(range.begin()))->slice_z;
auto it_layer_range = layer_range_first(layer_ranges, z);
// MM segmentation channels correspond to filament IDs (1-based), which now
// include enabled mixed / virtual filaments.
const size_t num_extruders = segmentation.empty() ? 0 : segmentation.front().size();
// MM segmentation channel 0 is the underlying / default color of the parent
// region. Remaining channels correspond to filament IDs (1-based), which
// now include enabled mixed / virtual filaments.
const size_t num_channels = segmentation.empty() ? 0 : segmentation.front().size();
const size_t num_extruders = num_channels > 0 ? num_channels - 1 : 0;
struct ByExtruder {
ExPolygons expolygons;
BoundingBox bbox;
};
auto intersect_surfaces_preserve_types = [](const SurfaceCollection &src, const ExPolygons &mask) {
SurfaceCollection out;
if (src.empty() || mask.empty())
return out;
std::array<SurfacesPtr, size_t(stCount)> by_surface;
for (const Surface &surface : src.surfaces)
by_surface[size_t(surface.surface_type)].emplace_back(&surface);
for (size_t surface_type = 0; surface_type < size_t(stCount); ++surface_type) {
const SurfacesPtr &typed_surfaces = by_surface[surface_type];
if (typed_surfaces.empty())
continue;
ExPolygons clipped = intersection_ex(typed_surfaces, mask);
if (!clipped.empty())
out.append(std::move(clipped), SurfaceType(surface_type));
}
return out;
};
struct ByRegion {
ExPolygons expolygons;
SurfaceCollection surfaces;
bool needs_merge { false };
};
auto normalize_region_surfaces = [](SurfaceCollection &src) {
if (src.surfaces.empty())
return;
std::array<ExPolygons, size_t(stCount)> by_surface;
for (Surface &surface : src.surfaces)
by_surface[size_t(surface.surface_type)].emplace_back(std::move(surface.expolygon));
src.surfaces.clear();
for (size_t surface_type = 0; surface_type < size_t(stCount); ++surface_type) {
ExPolygons &typed = by_surface[surface_type];
if (typed.empty())
continue;
if (typed.size() > 1)
typed = closing_ex(std::move(typed), scaled<float>(10. * EPSILON));
src.append(std::move(typed), SurfaceType(surface_type));
}
};
std::vector<ByExtruder> by_extruder;
std::vector<ByRegion> by_region;
for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
@@ -2987,14 +3072,20 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer.slice_z);
const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range;
// Gather per extruder expolygons.
assert(segmentation[layer_id].size() == num_extruders);
assert(segmentation[layer_id].size() == num_channels);
by_extruder.assign(num_extruders, ByExtruder());
by_region.assign(layer.region_count(), ByRegion());
bool layer_split = false;
size_t missing_target_regions = 0;
std::vector<int> missing_target_extruders;
for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id) {
const unsigned int channel_id = unsigned(extruder_id + 1);
ExPolygons default_segmentation = num_channels > 0 ? std::move(segmentation[layer_id][0]) : ExPolygons();
BoundingBox default_bbox;
if (!default_segmentation.empty()) {
default_bbox = get_extents(default_segmentation);
layer_split = true;
}
for (size_t channel_idx = 1; channel_idx < num_channels; ++ channel_idx) {
const unsigned int channel_id = unsigned(channel_idx);
const unsigned int effective_filament_id = collapse_mixed_regions ?
mixed_mgr.effective_painted_region_filament_id(channel_id,
num_physical,
@@ -3006,9 +3097,9 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
float(base_height)) :
channel_id;
const size_t effective_idx =
effective_filament_id >= 1 && effective_filament_id <= num_extruders ? size_t(effective_filament_id - 1) : extruder_id;
effective_filament_id >= 1 && effective_filament_id <= num_extruders ? size_t(effective_filament_id - 1) : size_t(channel_idx - 1);
ByExtruder &region = by_extruder[effective_idx];
append(region.expolygons, std::move(segmentation[layer_id][extruder_id]));
append(region.expolygons, std::move(segmentation[layer_id][channel_idx]));
if (! region.expolygons.empty()) {
region.bbox = get_extents(region.expolygons);
layer_split = true;
@@ -3022,22 +3113,31 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
// layer_range.painted_regions are sorted by extruder ID and parent PrintObject region ID.
auto it_painted_region_begin = layer_range.painted_regions.cbegin();
for (int parent_layer_region_idx = 0; parent_layer_region_idx < layer.region_count(); ++parent_layer_region_idx) {
if (it_painted_region_begin == layer_range.painted_regions.cend())
continue;
const LayerRegion &parent_layer_region = *layer.get_region(parent_layer_region_idx);
const PrintRegion &parent_print_region = parent_layer_region.region();
assert(parent_print_region.print_object_region_id() == parent_layer_region_idx);
if (parent_layer_region.slices.empty())
continue;
auto preserve_parent_region = [&by_region, &parent_layer_region, &parent_print_region]() {
if (!parent_layer_region.slices.empty())
by_region[parent_print_region.print_object_region_id()].surfaces = parent_layer_region.slices;
};
if (it_painted_region_begin == layer_range.painted_regions.cend()) {
preserve_parent_region();
continue;
}
// Find the first PaintedRegion, which overrides the parent PrintRegion.
auto it_first_painted_region = std::find_if(it_painted_region_begin, layer_range.painted_regions.cend(), [&layer_range, &parent_print_region](const auto &painted_region) {
return layer_range.volume_regions[painted_region.parent].region->print_object_region_id() == parent_print_region.print_object_region_id();
});
if (it_first_painted_region == layer_range.painted_regions.cend())
if (it_first_painted_region == layer_range.painted_regions.cend()) {
preserve_parent_region();
continue; // This LayerRegion isn't overrides by any PaintedRegion.
}
assert(&parent_print_region == layer_range.volume_regions[it_first_painted_region->parent].region);
@@ -3045,11 +3145,14 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
it_painted_region_begin = it_first_painted_region;
const BoundingBox parent_layer_region_bbox = get_extents(parent_layer_region.slices.surfaces);
bool self_trimmed = false;
int self_extruder_id = -1; // 1-based extruder ID
ExPolygons explicit_self_expolygons;
ExPolygons default_self_expolygons;
if (const int cfg_wall = parent_print_region.config().wall_filament.value;
cfg_wall >= 1 && cfg_wall <= int(by_extruder.size()))
self_extruder_id = cfg_wall;
if (default_bbox.defined && parent_layer_region_bbox.overlap(default_bbox))
default_self_expolygons = intersection_ex(parent_layer_region.slices.surfaces, default_segmentation);
std::vector<bool> assigned_extruder(by_extruder.size(), false);
std::vector<int> alias_to_self_extruders;
for (int extruder_id = 1; extruder_id <= int(by_extruder.size()); ++extruder_id) {
@@ -3078,6 +3181,13 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
self_extruder_id = extruder_id;
if (extruder_id != self_extruder_id)
alias_to_self_extruders.emplace_back(extruder_id);
ExPolygons self_segmented = intersection_ex(parent_layer_region.slices.surfaces, segmented.expolygons);
if (!self_segmented.empty()) {
if (explicit_self_expolygons.empty())
explicit_self_expolygons = std::move(self_segmented);
else
append(explicit_self_expolygons, std::move(self_segmented));
}
continue;
}
@@ -3088,44 +3198,74 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
ExPolygons stolen = intersection_ex(parent_layer_region.slices.surfaces, segmented.expolygons);
if (!stolen.empty()) {
ByRegion &dst = by_region[target_region_id];
if (dst.expolygons.empty()) {
dst.expolygons = std::move(stolen);
SurfaceCollection stolen_surfaces = intersect_surfaces_preserve_types(parent_layer_region.slices, stolen);
if (stolen_surfaces.empty())
continue;
if (dst.surfaces.empty()) {
dst.surfaces = std::move(stolen_surfaces);
} else {
append(dst.expolygons, std::move(stolen));
dst.surfaces.append(std::move(stolen_surfaces));
dst.needs_merge = true;
}
}
}
if (!self_trimmed) {
// Trim slices of this LayerRegion with all the MM regions.
Polygons mine = to_polygons(parent_layer_region.slices.surfaces);
for (size_t extruder_idx = 0; extruder_idx < by_extruder.size(); ++extruder_idx) {
const ByExtruder &segmented = by_extruder[extruder_idx];
if (!assigned_extruder[extruder_idx])
continue;
if (int(extruder_idx + 1) != self_extruder_id && segmented.bbox.defined && parent_layer_region_bbox.overlap(segmented.bbox)) {
mine = diff(mine, segmented.expolygons);
if (mine.empty())
break;
}
// Trim slices of this LayerRegion with all the MM regions.
Polygons mine = to_polygons(parent_layer_region.slices.surfaces);
for (size_t extruder_idx = 0; extruder_idx < by_extruder.size(); ++extruder_idx) {
const ByExtruder &segmented = by_extruder[extruder_idx];
if (!assigned_extruder[extruder_idx])
continue;
if (int(extruder_idx + 1) != self_extruder_id && segmented.bbox.defined && parent_layer_region_bbox.overlap(segmented.bbox)) {
mine = diff(mine, segmented.expolygons);
if (mine.empty())
break;
}
}
if (!explicit_self_expolygons.empty())
explicit_self_expolygons = union_ex(explicit_self_expolygons);
if (!default_self_expolygons.empty())
default_self_expolygons = union_ex(default_self_expolygons);
ExPolygons preserved_self_expolygons;
if (!explicit_self_expolygons.empty())
append(preserved_self_expolygons, explicit_self_expolygons);
if (!default_self_expolygons.empty())
append(preserved_self_expolygons, default_self_expolygons);
if (!preserved_self_expolygons.empty())
preserved_self_expolygons = union_ex(preserved_self_expolygons);
ExPolygons mine_expolygons;
if (!mine.empty()) {
if (!preserved_self_expolygons.empty())
mine = diff(mine, preserved_self_expolygons);
// Filter out unprintable polygons produced by subtraction multi-material painted regions from layerm.region().
// ExPolygon returned from multi-material segmentation does not precisely match ExPolygons in layerm.region()
// (because of preprocessing of the input regions in multi-material segmentation). Therefore, subtraction from
// layerm.region() could produce a huge number of small unprintable regions for the model's base extruder.
// This could, on some models, produce bulges with the model's base color (#7109).
if (!mine.empty()) {
if (!mine.empty())
mine = opening(union_ex(mine), scaled<float>(5. * EPSILON), scaled<float>(5. * EPSILON));
}
if (!mine.empty()) {
if (!mine.empty())
mine_expolygons = union_ex(mine);
}
if (!preserved_self_expolygons.empty()) {
append(mine_expolygons, preserved_self_expolygons);
mine_expolygons = union_ex(mine_expolygons);
}
if (!mine_expolygons.empty()) {
SurfaceCollection mine_surfaces = intersect_surfaces_preserve_types(parent_layer_region.slices, mine_expolygons);
if (!mine_surfaces.empty()) {
ByRegion &dst = by_region[parent_print_region.print_object_region_id()];
if (dst.expolygons.empty()) {
dst.expolygons = union_ex(mine);
if (dst.surfaces.empty()) {
dst.surfaces = std::move(mine_surfaces);
} else {
append(dst.expolygons, union_ex(mine));
dst.surfaces.append(std::move(mine_surfaces));
dst.needs_merge = true;
}
}
@@ -3172,15 +3312,428 @@ static inline void apply_mm_segmentation(PrintObject &print_object, std::vector<
ByRegion &src = by_region[region_id];
if (src.needs_merge) {
// Multiple regions were merged into one.
src.expolygons = closing_ex(src.expolygons, scaled<float>(10. * EPSILON));
normalize_region_surfaces(src.surfaces);
}
layer.get_region(region_id)->slices.set(std::move(src.expolygons), stInternal);
layer.get_region(region_id)->slices.set(std::move(src.surfaces));
}
dump_surface_emboss_mixed_layer_state("post-mm-segmentation",
print_object,
layer_id,
layer,
layer_range,
&segmentation[layer_id]);
}
});
}
static float emboss_surface_mixed_shell_override_delta(const LayerRegion &layerm, const ModelVolume &volume);
struct SurfaceEmbossMixedDebugCandidate
{
const ModelVolume *volume { nullptr };
int region_id { -1 };
};
static bool has_surface_emboss_mixed_volume(const PrintObject &print_object)
{
const Print *print = print_object.print();
if (print == nullptr)
return false;
const size_t num_physical = print->config().filament_diameter.size();
const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager();
for (const ModelVolume *volume : print_object.model_object()->volumes)
if (volume->is_model_part() &&
volume->emboss_shape.has_value() &&
volume->emboss_shape->projection.use_surface &&
mixed_mgr.is_mixed(unsigned(std::max(0, volume->extruder_id())), num_physical))
return true;
return false;
}
static std::string surface_emboss_mixed_debug_file_path(const PrintObject &print_object)
{
return debug_out_path("emboss-mixed/obj-%d-debug.txt", int(print_object.id().id));
}
static void reset_surface_emboss_mixed_debug_file(const PrintObject &print_object)
{
std::ofstream out(surface_emboss_mixed_debug_file_path(print_object), std::ios::out | std::ios::trunc);
out << "surface emboss mixed debug"
<< " object_id=" << int(print_object.id().id)
<< " object_name=" << (print_object.model_object() ? print_object.model_object()->name : std::string("<unknown>"))
<< "\n";
}
static void append_surface_emboss_mixed_debug_line(const PrintObject &print_object, const std::string &line)
{
std::ofstream out(surface_emboss_mixed_debug_file_path(print_object), std::ios::out | std::ios::app);
out << line << '\n';
}
static std::vector<SurfaceEmbossMixedDebugCandidate> collect_surface_emboss_mixed_debug_candidates(
const Layer &layer,
const PrintObjectRegions::LayerRangeRegions &layer_range,
const MixedFilamentManager &mixed_mgr,
size_t num_physical)
{
std::vector<SurfaceEmbossMixedDebugCandidate> out;
std::vector<int> processed_region_ids;
processed_region_ids.reserve(layer_range.volume_regions.size());
for (const PrintObjectRegions::VolumeRegion &volume_region : layer_range.volume_regions) {
const ModelVolume *volume = volume_region.model_volume;
if (volume == nullptr || !volume->is_model_part() || !volume->emboss_shape.has_value() || !volume->emboss_shape->projection.use_surface)
continue;
if (volume_region.region == nullptr)
continue;
const int region_id = volume_region.region->print_object_region_id();
if (region_id < 0 || region_id >= layer.region_count())
continue;
if (std::find(processed_region_ids.begin(), processed_region_ids.end(), region_id) != processed_region_ids.end())
continue;
processed_region_ids.emplace_back(region_id);
if (!mixed_mgr.is_mixed(unsigned(std::max(0, volume_region.region->config().wall_filament.value)), num_physical))
continue;
out.push_back({ volume, region_id });
}
return out;
}
static void export_surface_emboss_mixed_layer_svg(
const char *stage,
const PrintObject &print_object,
size_t layer_id,
const Layer &layer,
const std::vector<SurfaceEmbossMixedDebugCandidate> &candidates,
const ExPolygons *overlay,
const std::string &overlay_legend)
{
std::vector<std::pair<ExPolygons, SVG::ExPolygonAttributes>> items;
items.reserve(size_t(layer.region_count()) + ((overlay != nullptr && !overlay->empty()) ? 1 : 0));
for (int region_id = 0; region_id < layer.region_count(); ++region_id) {
const LayerRegion *layerm = layer.get_region(region_id);
if (layerm == nullptr || layerm->slices.empty())
continue;
ExPolygons expolygons = to_expolygons(layerm->slices.surfaces);
if (expolygons.empty())
continue;
const bool is_candidate = std::find_if(candidates.begin(), candidates.end(), [region_id](const auto &candidate) {
return candidate.region_id == region_id;
}) != candidates.end();
SVG::ExPolygonAttributes attrs(
"region " + std::to_string(region_id) + " wall=" + std::to_string(layerm->region().config().wall_filament.value),
is_candidate ? "#3b82f6" : "#bfc5cc",
is_candidate ? 0.35f : 0.14f);
attrs.outline_width = scale_(0.05f);
attrs.color_contour = is_candidate ? "blue" : "black";
attrs.color_holes = attrs.color_contour;
items.emplace_back(std::move(expolygons), std::move(attrs));
}
if (overlay != nullptr && !overlay->empty()) {
SVG::ExPolygonAttributes attrs(overlay_legend, "#ef4444", 0.28f);
attrs.outline_width = scale_(0.05f);
attrs.color_contour = "red";
attrs.color_holes = "red";
items.emplace_back(*overlay, std::move(attrs));
}
if (!items.empty())
SVG::export_expolygons(debug_out_path("emboss-mixed/obj-%d-layer-%03d-%s.svg",
int(print_object.id().id),
int(layer_id),
stage),
items);
}
static void dump_surface_emboss_mixed_layer_state(
const char *stage,
const PrintObject &print_object,
size_t layer_id,
const Layer &layer,
const PrintObjectRegions::LayerRangeRegions &layer_range,
const std::vector<ExPolygons> *segmentation_layer = nullptr)
{
const Print *print = print_object.print();
if (print == nullptr)
return;
const size_t num_physical = print->config().filament_diameter.size();
const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager();
const std::vector<SurfaceEmbossMixedDebugCandidate> candidates =
collect_surface_emboss_mixed_debug_candidates(layer, layer_range, mixed_mgr, num_physical);
if (candidates.empty())
return;
std::ostringstream header;
header << std::fixed << std::setprecision(4)
<< "stage=" << stage
<< " layer=" << layer_id
<< " print_z=" << layer.print_z
<< " slice_z=" << layer.slice_z
<< " regions=" << layer.region_count()
<< " candidates=" << candidates.size();
append_surface_emboss_mixed_debug_line(print_object, header.str());
for (int region_id = 0; region_id < layer.region_count(); ++region_id) {
const LayerRegion *layerm = layer.get_region(region_id);
if (layerm == nullptr)
continue;
const double slice_area = layerm->slices.empty() ? 0.0 : std::abs(area(to_expolygons(layerm->slices.surfaces)));
std::ostringstream line;
line << std::fixed << std::setprecision(4)
<< " region=" << region_id
<< " wall=" << layerm->region().config().wall_filament.value
<< " sparse=" << layerm->region().config().sparse_infill_filament.value
<< " solid=" << layerm->region().config().solid_infill_filament.value
<< " area=" << slice_area;
append_surface_emboss_mixed_debug_line(print_object, line.str());
}
for (const SurfaceEmbossMixedDebugCandidate &candidate : candidates) {
const LayerRegion *layerm = layer.get_region(candidate.region_id);
if (layerm == nullptr)
continue;
const double slice_area = layerm->slices.empty() ? 0.0 : std::abs(area(to_expolygons(layerm->slices.surfaces)));
const float shell_delta_scaled = emboss_surface_mixed_shell_override_delta(*layerm, *candidate.volume);
std::ostringstream line;
line << std::fixed << std::setprecision(4)
<< " candidate region=" << candidate.region_id
<< " volume_name=" << candidate.volume->name
<< " volume_extruder=" << candidate.volume->extruder_id()
<< " cfg_wall=" << layerm->region().config().wall_filament.value
<< " depth=" << float(candidate.volume->emboss_shape->projection.depth)
<< " shell_delta_mm=" << unscale<double>(shell_delta_scaled)
<< " area=" << slice_area;
append_surface_emboss_mixed_debug_line(print_object, line.str());
if (segmentation_layer != nullptr) {
const int cfg_wall = layerm->region().config().wall_filament.value;
if (cfg_wall >= 1 && cfg_wall <= int(segmentation_layer->size())) {
const double seg_area = std::abs(area((*segmentation_layer)[size_t(cfg_wall - 1)]));
std::ostringstream seg_line;
seg_line << std::fixed << std::setprecision(4)
<< " segmentation channel=" << cfg_wall
<< " area=" << seg_area;
append_surface_emboss_mixed_debug_line(print_object, seg_line.str());
}
}
}
export_surface_emboss_mixed_layer_svg(stage, print_object, layer_id, layer, candidates, nullptr, "");
}
static float emboss_surface_mixed_shell_override_delta(const LayerRegion &layerm, const ModelVolume &volume)
{
if (!volume.emboss_shape.has_value() || !volume.emboss_shape->projection.use_surface)
return 0.f;
const float depth_mm = std::max(0.f, float(volume.emboss_shape->projection.depth));
if (depth_mm <= EPSILON)
return 0.f;
const PrintRegionConfig &config = layerm.region().config();
if (config.wall_loops.value <= 0)
return 0.f;
const Flow ext_flow = layerm.flow(frExternalPerimeter);
const Flow perimeter_flow = layerm.flow(frPerimeter);
const coord_t shell_scaled = ext_flow.scaled_width() / 2 +
ext_flow.scaled_spacing() / 2 +
std::max(0, config.wall_loops.value - 1) * perimeter_flow.scaled_spacing();
const float shell_depth_mm = float(unscale<double>(shell_scaled));
const float delta_mm = std::max(0.f, shell_depth_mm - depth_mm);
return delta_mm <= EPSILON ? 0.f : scaled<float>(delta_mm);
}
template<typename ThrowOnCancel>
static bool apply_surface_emboss_mixed_region_override(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
{
const Print *print = print_object.print();
if (print == nullptr || print_object.layer_count() == 0 || print_object.shared_regions() == nullptr)
return false;
const size_t num_physical = print->config().filament_diameter.size();
const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager();
const auto &volumes = print_object.model_object()->volumes;
if (num_physical == 0 ||
std::find_if(volumes.begin(), volumes.end(), [&mixed_mgr, num_physical](const ModelVolume *volume) {
return volume->is_model_part() &&
volume->emboss_shape.has_value() &&
volume->emboss_shape->projection.use_surface &&
mixed_mgr.is_mixed(unsigned(std::max(0, volume->extruder_id())), num_physical);
}) == volumes.end())
return false;
const auto &layer_ranges = print_object.shared_regions()->layer_ranges;
auto it_layer_range = layer_range_first(layer_ranges, print_object.get_layer(0)->slice_z);
size_t changed_layers = 0;
size_t changed_regions = 0;
size_t stolen_regions = 0;
for (size_t layer_id = 0; layer_id < print_object.layer_count(); ++layer_id) {
throw_on_cancel();
Layer &layer = *print_object.get_layer(int(layer_id));
it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer.slice_z);
const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range;
const std::vector<SurfaceEmbossMixedDebugCandidate> candidates =
collect_surface_emboss_mixed_debug_candidates(layer, layer_range, mixed_mgr, num_physical);
if (!candidates.empty())
dump_surface_emboss_mixed_layer_state("pre-emboss-override", print_object, layer_id, layer, layer_range);
bool layer_changed = false;
ExPolygons layer_masks;
std::vector<int> processed_region_ids;
processed_region_ids.reserve(layer_range.volume_regions.size());
for (const PrintObjectRegions::VolumeRegion &volume_region : layer_range.volume_regions) {
const ModelVolume *volume = volume_region.model_volume;
if (volume == nullptr || !volume->is_model_part() || !volume->emboss_shape.has_value() || !volume->emboss_shape->projection.use_surface)
continue;
if (volume_region.region == nullptr)
continue;
const int region_id = volume_region.region->print_object_region_id();
if (region_id < 0 || region_id >= layer.region_count())
continue;
if (std::find(processed_region_ids.begin(), processed_region_ids.end(), region_id) != processed_region_ids.end())
continue;
processed_region_ids.emplace_back(region_id);
const unsigned int filament_id = unsigned(std::max(0, volume_region.region->config().wall_filament.value));
if (!mixed_mgr.is_mixed(filament_id, num_physical))
continue;
LayerRegion *emboss_layerm = layer.get_region(region_id);
if (emboss_layerm == nullptr || emboss_layerm->slices.empty())
continue;
ExPolygons override_mask = to_expolygons(emboss_layerm->slices.surfaces);
if (override_mask.empty())
continue;
if (const float delta_scaled = emboss_surface_mixed_shell_override_delta(*emboss_layerm, *volume);
delta_scaled > float(EPSILON)) {
override_mask = offset_ex(override_mask, delta_scaled);
if (override_mask.empty())
continue;
if (layer_masks.empty())
layer_masks = collect_layer_region_slices(layer);
if (!layer_masks.empty())
override_mask = intersection_ex(override_mask, layer_masks);
if (override_mask.empty())
continue;
}
{
std::ostringstream line;
line << std::fixed << std::setprecision(4)
<< "stage=override-mask"
<< " layer=" << layer_id
<< " region=" << region_id
<< " volume_name=" << volume->name
<< " volume_extruder=" << volume->extruder_id()
<< " cfg_wall=" << volume_region.region->config().wall_filament.value
<< " depth=" << float(volume->emboss_shape->projection.depth)
<< " shell_delta_mm=" << unscale<double>(emboss_surface_mixed_shell_override_delta(*emboss_layerm, *volume))
<< " mask_area=" << std::abs(area(override_mask));
append_surface_emboss_mixed_debug_line(print_object, line.str());
}
const std::string overlay_stage = "override-mask-r" + std::to_string(region_id);
export_surface_emboss_mixed_layer_svg(overlay_stage.c_str(),
print_object,
layer_id,
layer,
candidates,
&override_mask,
"override mask");
ExPolygons emboss_slices = to_expolygons(emboss_layerm->slices.surfaces);
bool emboss_changed = false;
for (int target_region_id = 0; target_region_id < layer.region_count(); ++target_region_id) {
if (target_region_id == region_id)
continue;
LayerRegion *target_layerm = layer.get_region(target_region_id);
if (target_layerm == nullptr || target_layerm->slices.empty())
continue;
if (target_layerm->region().config().wall_filament.value == int(filament_id))
continue;
ExPolygons stolen = intersection_ex(target_layerm->slices.surfaces, override_mask);
if (stolen.empty())
continue;
append(emboss_slices, stolen);
emboss_changed = true;
++stolen_regions;
std::ostringstream line;
line << std::fixed << std::setprecision(4)
<< "stage=override-steal"
<< " layer=" << layer_id
<< " emboss_region=" << region_id
<< " from_region=" << target_region_id
<< " from_wall=" << target_layerm->region().config().wall_filament.value
<< " stolen_area=" << std::abs(area(stolen));
append_surface_emboss_mixed_debug_line(print_object, line.str());
Polygons remaining = diff(to_polygons(target_layerm->slices.surfaces), override_mask);
if (!remaining.empty())
remaining = opening(union_ex(remaining), scaled<float>(5. * EPSILON), scaled<float>(5. * EPSILON));
target_layerm->slices.set(union_ex(remaining), stInternal);
layer_changed = true;
}
if (emboss_changed) {
if (emboss_slices.size() > 1)
emboss_slices = closing_ex(emboss_slices, scaled<float>(10. * EPSILON));
emboss_layerm->slices.set(std::move(emboss_slices), stInternal);
++changed_regions;
layer_changed = true;
} else {
std::ostringstream line;
line << "stage=override-no-steal"
<< " layer=" << layer_id
<< " region=" << region_id
<< " volume_name=" << volume->name;
append_surface_emboss_mixed_debug_line(print_object, line.str());
}
}
if (!candidates.empty())
dump_surface_emboss_mixed_layer_state("post-emboss-override", print_object, layer_id, layer, layer_range);
if (layer_changed)
++changed_layers;
}
if (changed_regions == 0)
return false;
BOOST_LOG_TRIVIAL(warning) << "Surface emboss mixed-region override applied"
<< " object=" << (print_object.model_object() ? print_object.model_object()->name : std::string("<unknown>"))
<< " changed_layers=" << changed_layers
<< " changed_regions=" << changed_regions
<< " stolen_regions=" << stolen_regions;
return true;
}
template<typename ThrowOnCancel>
void apply_fuzzy_skin_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
{
@@ -3398,6 +3951,8 @@ void PrintObject::slice_volumes()
apply_fuzzy_skin_segmentation(*this, [print]() { print->throw_if_canceled(); });
}
apply_surface_emboss_mixed_region_override(*this, [print]() { print->throw_if_canceled(); });
InterlockingGenerator::generate_interlocking_structure(this);
m_print->throw_if_canceled();
+7 -4
View File
@@ -16,16 +16,19 @@ add_executable(${_TEST_NAME}_tests
test_support_material.cpp
test_trianglemesh.cpp
)
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r OpenSSL::Crypto)
if (WIN32)
target_link_libraries(${_TEST_NAME}_tests bcrypt.lib)
endif()
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
if (WIN32)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
orcaslicer_copy_dlls(COPY_DLLS "Debug" "d" output_dlls_Debug)
Snapmaker_Orca_copy_dlls(COPY_DLLS "Debug" "d" output_dlls_Debug)
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
orcaslicer_copy_dlls(COPY_DLLS "RelWithDebInfo" "" output_dlls_Release)
Snapmaker_Orca_copy_dlls(COPY_DLLS "RelWithDebInfo" "" output_dlls_Release)
else()
orcaslicer_copy_dlls(COPY_DLLS "Release" "" output_dlls_Release)
Snapmaker_Orca_copy_dlls(COPY_DLLS "Release" "" output_dlls_Release)
endif()
endif()
+16 -16
View File
@@ -6,14 +6,14 @@
#include "test_data.hpp"
#include <algorithm>
#include <boost/regex.hpp>
#include <regex>
using namespace Slic3r;
using namespace Slic3r::Test;
boost::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
boost::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
boost::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
std::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
std::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
std::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
GIVEN("A default configuration and a print test object") {
@@ -61,16 +61,16 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
REQUIRE(gcode.find("; fill_density") != std::string::npos);
}
THEN("Infill is emitted.") {
boost::smatch has_match;
REQUIRE(boost::regex_search(gcode, has_match, infill_regex));
std::smatch has_match;
REQUIRE(std::regex_search(gcode, has_match, infill_regex));
}
THEN("Perimeters are emitted.") {
boost::smatch has_match;
REQUIRE(boost::regex_search(gcode, has_match, perimeters_regex));
std::smatch has_match;
REQUIRE(std::regex_search(gcode, has_match, perimeters_regex));
}
THEN("Skirt is emitted.") {
boost::smatch has_match;
REQUIRE(boost::regex_search(gcode, has_match, skirt_regex));
std::smatch has_match;
REQUIRE(std::regex_search(gcode, has_match, skirt_regex));
}
THEN("final Z height is 20mm") {
double final_z = 0.0;
@@ -100,16 +100,16 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
REQUIRE(gcode.size() > 0);
}
THEN("Infill is emitted.") {
boost::smatch has_match;
REQUIRE(boost::regex_search(gcode, has_match, infill_regex));
std::smatch has_match;
REQUIRE(std::regex_search(gcode, has_match, infill_regex));
}
THEN("Perimeters are emitted.") {
boost::smatch has_match;
REQUIRE(boost::regex_search(gcode, has_match, perimeters_regex));
std::smatch has_match;
REQUIRE(std::regex_search(gcode, has_match, perimeters_regex));
}
THEN("Skirt is emitted.") {
boost::smatch has_match;
REQUIRE(boost::regex_search(gcode, has_match, skirt_regex));
std::smatch has_match;
REQUIRE(std::regex_search(gcode, has_match, skirt_regex));
}
THEN("Between-object-gcode is emitted.") {
REQUIRE(gcode.find("; between-object-gcode") != std::string::npos);
+4 -1
View File
@@ -1,6 +1,9 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp printer_parts.cpp printer_parts.hpp)
target_link_libraries(${_TEST_NAME}_tests test_common libnest2d )
target_link_libraries(${_TEST_NAME}_tests test_common libnest2d OpenSSL::Crypto)
if (WIN32)
target_link_libraries(${_TEST_NAME}_tests bcrypt.lib)
endif()
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
+4 -1
View File
@@ -32,7 +32,10 @@ if (TARGET OpenVDB::openvdb)
target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp)
endif()
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r OpenSSL::Crypto)
if (WIN32)
target_link_libraries(${_TEST_NAME}_tests bcrypt.lib)
endif()
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
if (WIN32)
+3 -3
View File
@@ -66,7 +66,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
GIVEN("polyline") {
Polyline polyline { { 50, 150 }, { 300, 150 } };
WHEN("intersection_pl") {
Polylines result = Slic3r::intersection_pl({ polyline }, { square, hole_in_square });
Polylines result = Slic3r::intersection_pl(Polylines { polyline }, Polygons { square, hole_in_square });
THEN("correct number of result lines") {
REQUIRE(result.size() == 2);
}
@@ -99,7 +99,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
Slic3r::Polylines result = Slic3r::intersection_pl(subject, Polygons { clip });
THEN("intersection_pl - result is not empty") {
REQUIRE(result.size() == 1);
}
@@ -117,7 +117,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
GIVEN("Clipper bug #126") {
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
Slic3r::Polylines result = Slic3r::intersection_pl(subject, Polygons { clip });
THEN("intersection_pl - result is not empty") {
REQUIRE(result.size() == 1);
}