OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::PreciseSeam::detail Namespace Reference

Classes

struct  ClippedEdgeInterval
struct  FragmentBindingFailure

Functions

static std::optional< double > parameter_on_edge (const Point &point, const Point &a, const Point &b)
static std::optional< ClippedEdgeInterval > interval_on_edge (const Point &first, const Point &last, size_t edge, const Polygon &perimeter)
static bool append_exact_fragment (const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals)
bool append_projected_fragment (const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals, FragmentBindingFailure &failure)
bool bind_fragment (const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals, FragmentBindingFailure &failure)
static bool snap_cuts_to_adjacent_vertices (Polyline &fragment, const Polygon &perimeter)
static std::string fragment_location (const ExtractionContext &context, size_t fragment_index)
bool append_fragment (const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals, const ExtractionContext &context, size_t fragment_index)

Class Documentation

◆ Slic3r::PreciseSeam::detail::ClippedEdgeInterval

struct Slic3r::PreciseSeam::detail::ClippedEdgeInterval
Collaboration diagram for Slic3r::PreciseSeam::detail::ClippedEdgeInterval:
Class Members
double begin
size_t edge
double end
Point first
Point last

◆ Slic3r::PreciseSeam::detail::FragmentBindingFailure

struct Slic3r::PreciseSeam::detail::FragmentBindingFailure
Class Members
size_t pair_index = 0
const char * reason = "empty fragment"

Function Documentation

◆ append_exact_fragment()

bool Slic3r::PreciseSeam::detail::append_exact_fragment ( const Polyline & fragment,
const Polygon & perimeter,
std::vector< ClippedEdgeInterval > & intervals )
static
124{
125 const size_t size = fragment.size();
126 const size_t count = perimeter.size();
127 assert(size >= 3); // bind_fragment() sends only fragments with interior points here.
128 // A longer sequence cannot be one exact pass over this contour.
129 if (size > count + 2)
130 return false;
131 for (size_t anchor = 0; anchor < count; ++anchor) {
132 if (fragment.points[1] != perimeter.points[anchor])
133 continue;
134 // Clipping may return either direction, independent of contour winding.
135 for (bool forward : {true, false}) {
136 const auto next = [count, forward](size_t index) {
137 return forward ? (index + 1) % count : (index + count - 1) % count;
138 };
139 size_t last = anchor;
140 bool matches = true;
141 for (size_t i = 2; i + 1 < size; ++i) {
142 last = next(last);
143 if (fragment.points[i] != perimeter.points[last]) {
144 matches = false;
145 break;
146 }
147 }
148 if (!matches)
149 continue;
150 const size_t first_edge = forward ? (anchor + count - 1) % count : anchor;
151 const size_t last_edge = forward ? last : (last + count - 1) % count;
152 const auto first = interval_on_edge(fragment.points.front(), fragment.points[1], first_edge, perimeter);
153 const auto end = interval_on_edge(fragment.points[size - 2], fragment.points.back(), last_edge, perimeter);
154 if (!first || !end)
155 continue;
156 // Commit only a complete match, so rejected anchors leave no intervals.
157 intervals.push_back(*first);
158 size_t vertex = anchor;
159 for (size_t i = 2; i + 1 < size; ++i) {
160 const size_t adjacent = next(vertex);
161 const size_t edge = forward ? vertex : adjacent;
162 intervals.push_back({edge, 0., 1., perimeter.points[edge], perimeter.points[(edge + 1) % count]});
163 vertex = adjacent;
164 }
165 intervals.push_back(*end);
166 return true;
167 }
168 }
169 return false;
170}
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
Points points
Definition MultiPoint.hpp:22
size_t size() const
Definition MultiPoint.hpp:50
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
static std::optional< ClippedEdgeInterval > interval_on_edge(const Point &first, const Point &last, size_t edge, const Polygon &perimeter)
Definition PreciseSeam.cpp:97

References count, interval_on_edge(), Slic3r::MultiPoint::points, and Slic3r::MultiPoint::size().

Referenced by bind_fragment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ append_fragment()

bool Slic3r::PreciseSeam::detail::append_fragment ( const Polyline & fragment,
const Polygon & perimeter,
std::vector< ClippedEdgeInterval > & intervals,
const ExtractionContext & context,
size_t fragment_index )
324{
326 if (bind_fragment(fragment, perimeter, intervals, failure))
327 return true;
328 // Rare-case fallback: repair the end cuts and retry once. A recovery is logged but is not a failure
329 // (no failure count, no user warning).
330 const char *outcome = nullptr;
331 Polyline cleaned = fragment;
332 if (snap_cuts_to_adjacent_vertices(cleaned, perimeter)) {
333 FragmentBindingFailure retry_failure;
334 // Only a contact shorter than the snapping distance may remain: nothing to bind, nothing lost.
335 if (cleaned.size() < 2)
336 outcome = "contact";
337 else if (bind_fragment(cleaned, perimeter, intervals, retry_failure))
338 outcome = "bound";
339 }
340 // A failed fragment shorter than the snapping radius is a contact: insertion would collapse it to one
341 // point anyway, so accept it instead of reporting a failure.
342 if (outcome == nullptr && fragment.length() < double(TOLERANCE_LINEAR))
343 outcome = "contact";
344 if (outcome != nullptr) {
345 // Same log budget as failures, counted separately; SeamPlacer::init() reports the total.
346 if (context.warnings == nullptr ||
347 context.warnings->recovered_fragments.fetch_add(1, std::memory_order_relaxed) < failed_fragment_log_limit)
348 BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamFragmentRecovered] Rare case resolved after a binding failure"
349 << " outcome=" << outcome << fragment_location(context, fragment_index)
350 << " original_pair=" << failure.pair_index << " original_reason=" << failure.reason
351 << " fragment_points=" << fragment.size() << " cleaned_points=" << cleaned.size()
352 << " perimeter_points=" << perimeter.size();
353 return true;
354 }
355 // Reserve a log slot atomically before formatting; callers without shared state log every failure.
356 if (context.warnings &&
357 context.warnings->failed_fragments.fetch_add(1, std::memory_order_relaxed) >= failed_fragment_log_limit)
358 return false;
359 // Keep a small marker for investigating a saved project, not a full geometry dump.
360 BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamIntersectionFailed] Unable to process intersection"
361 << fragment_location(context, fragment_index) << " pair=" << failure.pair_index
362 << " reason=" << failure.reason << " fragment_points=" << fragment.size()
363 << " perimeter_points=" << perimeter.size();
364 return false;
365}
double length() const
Definition MultiPoint.cpp:59
Definition Polyline.hpp:25
const char * reason
Definition PreciseSeamInternal.hpp:23
static std::string fragment_location(const ExtractionContext &context, size_t fragment_index)
Definition PreciseSeam.cpp:310
bool bind_fragment(const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals, FragmentBindingFailure &failure)
Definition PreciseSeam.cpp:236
static bool snap_cuts_to_adjacent_vertices(Polyline &fragment, const Polygon &perimeter)
Definition PreciseSeam.cpp:248
size_t pair_index
Definition PreciseSeamInternal.hpp:22
Definition PreciseSeamInternal.hpp:21
PreciseSeamWarnings * warnings
Definition PreciseSeam.hpp:93
static constexpr coord_t TOLERANCE_LINEAR
Definition PreciseSeam.cpp:74
constexpr size_t failed_fragment_log_limit
Definition PreciseSeam.hpp:45
std::atomic< size_t > failed_fragments
Definition PreciseSeam.hpp:54
std::atomic< size_t > recovered_fragments
Definition PreciseSeam.hpp:57

References bind_fragment(), Slic3r::PreciseSeam::failed_fragment_log_limit, Slic3r::PreciseSeam::PreciseSeamWarnings::failed_fragments, fragment_location(), Slic3r::MultiPoint::length(), Slic3r::PreciseSeam::detail::FragmentBindingFailure::pair_index, Slic3r::PreciseSeam::detail::FragmentBindingFailure::reason, Slic3r::PreciseSeam::PreciseSeamWarnings::recovered_fragments, Slic3r::MultiPoint::size(), snap_cuts_to_adjacent_vertices(), Slic3r::PreciseSeam::TOLERANCE_LINEAR, and Slic3r::PreciseSeam::ExtractionContext::warnings.

Referenced by Slic3r::PreciseSeam::extract_perimeter_segments().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ append_projected_fragment()

bool Slic3r::PreciseSeam::detail::append_projected_fragment ( const Polyline & fragment,
const Polygon & perimeter,
std::vector< ClippedEdgeInterval > & intervals,
FragmentBindingFailure & failure )
176{
177 const size_t original_size = intervals.size();
178 bool forward = true;
179 const size_t count = perimeter.size();
180 for (size_t i = 1; i < fragment.size(); ++i) {
181 if (fragment.points[i - 1] == fragment.points[i])
182 continue;
183 failure.pair_index = i - 1;
184 std::optional<ClippedEdgeInterval> matched;
185 const auto is_forward = [&](const ClippedEdgeInterval &candidate) {
186 const Vec2d movement = fragment.points[i].cast<double>() - fragment.points[i - 1].cast<double>();
187 const Vec2d edge = perimeter.points[(candidate.edge + 1) % count].cast<double>() -
188 perimeter.points[candidate.edge].cast<double>();
189 return movement.dot(edge) > 0.;
190 };
191 if (intervals.size() == original_size) {
192 failure.reason = "initial edge not found";
193 for (size_t edge = 0; edge < count; ++edge) {
194 matched = interval_on_edge(fragment.points[i - 1], fragment.points[i], edge, perimeter);
195 if (matched) {
196 // The first suitable edge defines orientation; overlapping visits are unsupported.
197 forward = is_forward(*matched);
198 break;
199 }
200 }
201 } else {
202 failure.reason = "non-continuous binding";
203 const ClippedEdgeInterval &previous = intervals.back();
204 const double end = forward ? previous.end : previous.begin;
205 matched = interval_on_edge(fragment.points[i - 1], fragment.points[i], previous.edge, perimeter);
206 if (matched && is_forward(*matched) != forward)
207 matched.reset();
208 // The shared point is the previous pair's last point on the same edge, so continuity holds by
209 // construction. Reuse its parameter rather than comparing a recomputed one: the two projections
210 // may be compiled differently (e.g. FMA contraction) and differ by an ulp on some platforms.
211 if (matched)
212 (forward ? matched->begin : matched->end) = end;
213 // Crossing to the neighbor is allowed only at their actual shared vertex.
214 const size_t vertex = forward ? (previous.edge + 1) % count : previous.edge;
215 if (!matched && end == (forward ? 1. : 0.) && fragment.points[i - 1] == perimeter.points[vertex]) {
216 const size_t edge = forward ? vertex : (previous.edge + count - 1) % count;
217 matched = interval_on_edge(fragment.points[i - 1], fragment.points[i], edge, perimeter);
218 if (matched && is_forward(*matched) != forward)
219 matched.reset();
220 // The shared point is exactly the neighbor's start (forward) or end (backward) vertex,
221 // so parameter_on_edge() returns exactly 0 or 1 for it.
222 assert(!matched || (forward ? matched->begin == 0. : matched->end == 1.));
223 }
224 }
225 if (!matched) {
226 // Never leave a partial fragment or discard earlier successful fragments.
227 intervals.resize(original_size);
228 return false;
229 }
230 intervals.push_back(*matched);
231 }
232 return intervals.size() > original_size;
233}
Definition PreciseSeamInternal.hpp:13
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299

References Slic3r::PreciseSeam::detail::ClippedEdgeInterval::begin, Slic3r::cast(), count, Slic3r::PreciseSeam::detail::ClippedEdgeInterval::edge, Slic3r::PreciseSeam::detail::ClippedEdgeInterval::end, interval_on_edge(), Slic3r::PreciseSeam::detail::FragmentBindingFailure::pair_index, Slic3r::MultiPoint::points, Slic3r::PreciseSeam::detail::FragmentBindingFailure::reason, and Slic3r::MultiPoint::size().

Referenced by bind_fragment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ bind_fragment()

bool Slic3r::PreciseSeam::detail::bind_fragment ( const Polyline & fragment,
const Polygon & perimeter,
std::vector< ClippedEdgeInterval > & intervals,
FragmentBindingFailure & failure )
238{
239 if (fragment.size() > 2 && append_exact_fragment(fragment, perimeter, intervals))
240 return true;
241 return append_projected_fragment(fragment, perimeter, intervals, failure);
242}
static bool append_exact_fragment(const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals)
Definition PreciseSeam.cpp:122
bool append_projected_fragment(const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals, FragmentBindingFailure &failure)
Definition PreciseSeam.cpp:173

References append_exact_fragment(), append_projected_fragment(), and Slic3r::MultiPoint::size().

Referenced by append_fragment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fragment_location()

std::string Slic3r::PreciseSeam::detail::fragment_location ( const ExtractionContext & context,
size_t fragment_index )
static
311{
312 const Layer *layer = context.layer;
313 const ModelObject *object = layer && layer->object() ? layer->object()->model_object() : nullptr;
314 return " object=" + std::to_string(object ? object->id().id : 0) +
315 " modifier=" + std::to_string(context.modifier ? context.modifier->id().id : 0) +
316 " layer=" + (layer ? std::to_string(layer->id()) : std::string("unknown")) +
317 " z=" + (layer ? std::to_string(layer->slice_z) : std::string("unknown")) +
318 " fragment=" + std::to_string(fragment_index);
319}
Definition Layer.hpp:138
size_t id() const
Definition Layer.hpp:141
PrintObject * object()
Definition Layer.hpp:143
Definition Model.hpp:382
ObjectID id() const
Definition ObjectID.hpp:69
size_t id
Definition ObjectID.hpp:39
const ModelObject * model_object() const
Definition PrintBase.hpp:346
const Layer * layer
Definition PreciseSeam.hpp:91
const ModelVolume * modifier
Definition PreciseSeam.hpp:92
std::string to_string(const Vec2crd &pt)
Definition Point.hpp:165
STL namespace.

References Slic3r::Layer::id(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::PreciseSeam::ExtractionContext::layer, Slic3r::PrintObjectBase::model_object(), Slic3r::PreciseSeam::ExtractionContext::modifier, Slic3r::Layer::object(), and Slic3r::Layer::slice_z.

Referenced by append_fragment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ interval_on_edge()

std::optional< ClippedEdgeInterval > Slic3r::PreciseSeam::detail::interval_on_edge ( const Point & first,
const Point & last,
size_t edge,
const Polygon & perimeter )
static
99{
100 const Point &a = perimeter.points[edge];
101 const Point &b = perimeter.points[(edge + 1) % perimeter.size()];
102 const auto t0 = parameter_on_edge(first, a, b);
103 // Reject this edge before projecting the second point when the first is outside.
104 if (!t0)
105 return std::nullopt;
106 const auto t1 = parameter_on_edge(last, a, b);
107 if (!t1 || *t0 == *t1)
108 return std::nullopt;
109 ClippedEdgeInterval interval{edge, *t0, *t1, first, last};
110 if (interval.begin > interval.end) {
111 std::swap(interval.begin, interval.end);
112 std::swap(interval.first, interval.last);
113 }
114 // Canonical endpoints make adjacent original edges join at their shared vertex.
115 if (interval.begin == 0.) interval.first = a;
116 if (interval.end == 1.) interval.last = b;
117 return interval;
118}
Definition Point.hpp:199
double end
Definition PreciseSeamInternal.hpp:16
static std::optional< double > parameter_on_edge(const Point &point, const Point &a, const Point &b)
Definition PreciseSeam.cpp:81
double begin
Definition PreciseSeamInternal.hpp:15
Point last
Definition PreciseSeamInternal.hpp:18
Point first
Definition PreciseSeamInternal.hpp:17

References Slic3r::PreciseSeam::detail::ClippedEdgeInterval::begin, Slic3r::PreciseSeam::detail::ClippedEdgeInterval::end, Slic3r::PreciseSeam::detail::ClippedEdgeInterval::first, Slic3r::PreciseSeam::detail::ClippedEdgeInterval::last, parameter_on_edge(), Slic3r::MultiPoint::points, and Slic3r::MultiPoint::size().

Referenced by append_exact_fragment(), and append_projected_fragment().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parameter_on_edge()

std::optional< double > Slic3r::PreciseSeam::detail::parameter_on_edge ( const Point & point,
const Point & a,
const Point & b )
static
82{
83 // Original vertices need no floating-point projection.
84 if (point == a) return 0.;
85 if (point == b) return 1.;
86 const Vec2d direction = b.cast<double>() - a.cast<double>();
87 const Vec2d offset = point.cast<double>() - a.cast<double>();
88 const double squared_length = direction.squaredNorm();
89 if (squared_length == 0.)
90 return std::nullopt;
91 const double parameter = std::clamp(offset.dot(direction) / squared_length, 0., 1.);
92 if ((offset - parameter * direction).squaredNorm() > MACHINE_PRECISION_SQUARED)
93 return std::nullopt;
94 return parameter;
95}
static constexpr double MACHINE_PRECISION_SQUARED
Definition PreciseSeam.cpp:70
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582

References Slic3r::PreciseSeam::MACHINE_PRECISION_SQUARED, and Slic3r::offset().

Referenced by interval_on_edge().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ snap_cuts_to_adjacent_vertices()

bool Slic3r::PreciseSeam::detail::snap_cuts_to_adjacent_vertices ( Polyline & fragment,
const Polygon & perimeter )
static
249{
250 const size_t count = perimeter.size();
251 // Strict comparison matches the vertex snapping in insert_point_into_perimeter.
252 const auto close = [](const Point &a, const Point &b) {
253 return (a - b).cast<double>().squaredNorm() < double(TOLERANCE_SQUARED);
254 };
255 const auto snap_target = [&](const Point &cut, const Point &neighbour) -> std::optional<Point> {
256 if (cut == neighbour)
257 return std::nullopt;
258 bool neighbour_is_vertex = false;
259 bool ambiguous = false;
260 std::optional<Point> target;
261 // Scan the whole perimeter before deciding: a cut that is a real vertex must be detected anywhere,
262 // and an ambiguity must not pre-empt the neighbour rule below.
263 for (size_t j = 0; j < count; ++j) {
264 if (perimeter[j] == cut)
265 return std::nullopt; // A real vertex, not a rounded cut.
266 if (perimeter[j] != neighbour)
267 continue;
268 neighbour_is_vertex = true;
269 for (const Point &candidate : {perimeter[(j + count - 1) % count], perimeter[(j + 1) % count]}) {
270 if (!close(cut, candidate))
271 continue;
272 if (target && *target != candidate)
273 ambiguous = true;
274 target = candidate;
275 }
276 }
277 // A cut between two arbitrary points of one edge is not this rounding case.
278 if (!neighbour_is_vertex)
279 return std::nullopt;
280 // The neighbour itself takes precedence, even when both of its chain vertices are close too
281 // (edges shorter than the radius on both sides): the cut is then just a rounded copy of it.
282 if (close(cut, neighbour))
283 return neighbour;
284 // Two different edge-start candidates: keep the failure rather than guess.
285 if (ambiguous)
286 return std::nullopt;
287 return target;
288 };
289 bool changed = false;
290 if (fragment.size() >= 2)
291 if (const auto target = snap_target(fragment.points.front(), fragment.points[1])) {
292 if (*target == fragment.points[1])
293 fragment.points.erase(fragment.points.begin());
294 else
295 fragment.points.front() = *target;
296 changed = true;
297 }
298 if (fragment.size() >= 2)
299 if (const auto target = snap_target(fragment.points.back(), fragment.points[fragment.size() - 2])) {
300 if (*target == fragment.points[fragment.size() - 2])
301 fragment.points.pop_back();
302 else
303 fragment.points.back() = *target;
304 changed = true;
305 }
306 return changed;
307}
static constexpr coord_t TOLERANCE_SQUARED
Definition PreciseSeam.cpp:75

References count, Slic3r::MultiPoint::points, Slic3r::MultiPoint::size(), and Slic3r::PreciseSeam::TOLERANCE_SQUARED.

Referenced by append_fragment().

Here is the call graph for this function:
Here is the caller graph for this function: