Precise Seam: new full-coverage policy for Seam Blocked (#16170)

* Precise Seam: new full-coverage policy for Seam Blocked

A Seam Blocked modifier that covers a whole perimeter now blocks it, like a
perimeter painted red all round, instead of being skipped. It overrides
painting and lower-priority modifiers, like Enforced and Neutral. A new
warning names the modifier: "Seam Blocked modifier "<name>" of "<object>"
covers an entire perimeter". It is shown only when a perimeter stays
entirely Blocked after all weak modifiers. The full-containment warning now
covers only Center, Left and Right.

* Precise Seam: fix seams on perimeters blocked all round

- SeamComparator::is_first_not_much_worse() compares the types first, so two
  Blocked candidates fall through like Neutral ones. On a perimeter blocked
  all round, by a whole Seam Blocked modifier or by red painting, Random put
  the seam on the closing vertex of every layer and Aligned stopped aligning.
- The whole-perimeter Blocked warning flags only the highest whole zone, the
  one in effect. A lower whole zone it overwrites is no longer reported, and
  the candidate scan runs only when that zone is Blocked.
- The warning msgids take the type name from the menu's "Seam Blocked" msgid.
- PreciseSeamWarnings::set() shares the load-before-store idiom, and every
  weak segment records its modifier.
- Tests: a whole Blocked perimeter gets the seams of an unmarked one under
  every seam position; overwritten whole Blocked zones are not reported.

---------

Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
This commit is contained in:
Damir GaleevandIan Bassi authored and GitHub committed 2026-10-13 16:38:08 -03:00
1 parent 008ee792f6
commit 5a427f2eb6
6 files changed
+243 -104

No files matched your search

+41 -20
View File
@@ -608,7 +608,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const
// Apply weak modifiers if no strong modifier was inserted
if (!inserted_seam_position.has_value() && !weak_segments.empty()) {
PreciseSeam::apply_weak_modifiers_to_perimeter(
weak_segments, result, perimeter, some_point_enforced);
weak_segments, result, perimeter, some_point_enforced, warnings);
}
if (some_point_enforced) {
@@ -896,18 +896,15 @@ struct SeamComparator {
return a.central_enforcer;
}
if (a.type == EnforcedBlockedSeamPoint::Enforced) {
return true;
}
if (a.type == EnforcedBlockedSeamPoint::Blocked) {
return false;
}
if (a.type != b.type) {
return a.type > b.type;
}
// Two Blocked points fall through like Neutral ones, so a perimeter blocked all round still gets a proper seam.
if (a.type == EnforcedBlockedSeamPoint::Enforced) {
return true;
}
//avoid overhangs
if ((a.overhang > 0.0f || b.overhang > 0.0f)
&& abs(a.overhang - b.overhang) > (0.1f * a.perimeter.flow_width)) {
@@ -1660,18 +1657,42 @@ void SeamPlacer::init(const Print &print, std::function<void(void)> throw_if_can
parts.push_back((boost::format(_u8L("multiple intersections with a perimeter, only one was used (%1%)")) % type_list(mi)).str());
if (fc != 0)
parts.push_back((boost::format(_u8L("a perimeter is fully inside a modifier, the modifier was not applied to it (%1%)")) % type_list(fc)).str());
// Modifiers evaluated somewhere that never reached a perimeter; never-evaluated ones are not reported.
// Print and volume order make the named one deterministic; the log lists them all.
std::vector<const ModelVolume*> no_effect;
for (const PrintObject *po : print.objects())
for (const ModelVolume *volume : po->model_object()->volumes) {
const auto it = precise_seam_warnings.modifier_usage.find(volume);
if (it != precise_seam_warnings.modifier_usage.end() &&
it->second.checked.load(std::memory_order_relaxed) &&
!it->second.reached.load(std::memory_order_relaxed) &&
std::find(no_effect.begin(), no_effect.end(), volume) == no_effect.end())
no_effect.push_back(volume);
// Modifiers whose usage flags match, in print and volume order, so the named one is deterministic.
const auto collect_modifiers = [&print, &precise_seam_warnings](const auto &matches) {
std::vector<const ModelVolume*> found;
for (const PrintObject *po : print.objects())
for (const ModelVolume *volume : po->model_object()->volumes) {
const auto it = precise_seam_warnings.modifier_usage.find(volume);
if (it != precise_seam_warnings.modifier_usage.end() && matches(it->second) &&
std::find(found.begin(), found.end(), volume) == found.end())
found.push_back(volume);
}
return found;
};
// Seam Blocked modifiers whose whole perimeter stayed Blocked; the user warning names only the
// first one, the log lists them all.
const std::vector<const ModelVolume*> fully_blocked = collect_modifiers(
[](const PreciseSeam::PreciseSeamWarnings::ModifierUsage &usage) { return usage.fully_blocked.load(std::memory_order_relaxed); });
for (const ModelVolume *volume : fully_blocked)
BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamFullyBlocked] object=\"" << volume->get_object()->name
<< "\" modifier=\"" << volume->name << "\"";
if (!fully_blocked.empty()) {
const ModelVolume *first = fully_blocked.front();
if (fully_blocked.size() == 1) {
// TRN: %1% is the modifier type "Seam Blocked" as in the menu, %2% the modifier name, %3% the object name.
parts.push_back((boost::format(_u8L("%1% modifier \"%2%\" of \"%3%\" covers an entire perimeter"))
% _u8L("Seam Blocked") % first->name % first->get_object()->name).str());
} else {
// TRN: %1% is the modifier type "Seam Blocked" as in the menu, %2% the modifier name, %3% the object name, %4% the number of modifiers.
parts.push_back((boost::format(_u8L("%1% modifier \"%2%\" of \"%3%\" (%4% in total) covers an entire perimeter"))
% _u8L("Seam Blocked") % first->name % first->get_object()->name % fully_blocked.size()).str());
}
}
// Modifiers evaluated somewhere that never reached a perimeter; never-evaluated ones are not reported.
const std::vector<const ModelVolume*> no_effect = collect_modifiers(
[](const PreciseSeam::PreciseSeamWarnings::ModifierUsage &usage) {
return usage.checked.load(std::memory_order_relaxed) && !usage.reached.load(std::memory_order_relaxed);
});
// The user warning names only the first one; the log lists them all.
for (const ModelVolume *volume : no_effect)
BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamNoEffect] object=\"" << volume->get_object()->name