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

Namespaces

namespace  detail

Classes

struct  ModifierRegion
struct  PreciseSeamWarnings
struct  ExtractionContext
struct  PreparedPerimeter
struct  PerimeterPosition
struct  StrongSeamTarget
struct  PerimeterSegment
struct  SegmentExtraction
struct  WeakModifierSegment

Typedefs

using ModifierRegions = std::vector<ModifierRegion>
using ModifierSlices = std::vector<ModifierRegions>
using ModifierRegionsCache = std::unordered_map<const ModelVolume*, ModifierSlices>

Enumerations

enum class  EnforcedBlockedSeamPoint

Functions

ModifierRegions prepare_modifier_regions (ExPolygons regions)
ModifierSlices prepare_modifier_slices (std::vector< ExPolygons > slices)
static void prepare_strong_segment (PerimeterSegment &segment, ModelVolumeType mode, bool compare_lengths)
SegmentExtraction extract_perimeter_segments (const PreparedPerimeter &prepared, const ModifierRegions &modifier, ModelVolumeType mode, const ExtractionContext &context)
void init_precise_seam_data (std::vector< const ModelVolume * > &strong_volumes_out, std::vector< const ModelVolume * > &weak_volumes_out, bool &has_strong_out, const ModelObject *model_object)
static std::pair< Point, size_t > insert_point_into_perimeter (const Point &point, size_t edge_start_idx, Polygon &perimeter_polygon)
static void refine_at_vertex (size_t point_idx, int direction, Polygon &perimeter_polygon)
static void record_extraction (PreciseSeamWarnings *warnings, const ModelVolume *modifier, const SegmentExtraction &extracted)
std::optional< Point > insert_strong_seam_point (const std::vector< const ModelVolume * > &strong_volumes, Polygon &polygon, const PreparedPerimeter &prepared, const Layer *layer, const ModifierRegionsCache &slices_cache, PreciseSeamWarnings *warnings)
static EnforcedBlockedSeamPoint convert_weak_modifier_type (ModelVolumeType type)
static std::vector< WeakModifierSegment > prepare_weak_modifier_segments (std::vector< WeakModifierSegment > result, Polygon &polygon)
std::vector< WeakModifierSegment > collect_weak_modifier_segments (const std::vector< const ModelVolume * > &weak_volumes, Polygon &polygon, const PreparedPerimeter &prepared, const Layer *layer, const ModifierRegionsCache &slices_cache, PreciseSeamWarnings *warnings)
void apply_weak_modifiers_to_perimeter (const std::vector< WeakModifierSegment > &weak_segments, PrintObjectSeamData::LayerSeams &result, const SeamPlacerImpl::Perimeter &perimeter, bool &some_point_enforced, PreciseSeamWarnings *warnings)
void restore_precise_seam_positions (std::vector< PrintObjectSeamData::LayerSeams > &layers)

Variables

static constexpr double MACHINE_PRECISION_SQUARED = 2.5
static constexpr coord_t TOLERANCE_LINEAR = 1000
static constexpr coord_t TOLERANCE_SQUARED = TOLERANCE_LINEAR * TOLERANCE_LINEAR
constexpr size_t failed_fragment_log_limit = 10

Class Documentation

◆ Slic3r::PreciseSeam::ExtractionContext

struct Slic3r::PreciseSeam::ExtractionContext
Collaboration diagram for Slic3r::PreciseSeam::ExtractionContext:
Class Members
const Layer * layer = nullptr
const ModelVolume * modifier = nullptr
PreciseSeamWarnings * warnings = nullptr

◆ Slic3r::PreciseSeam::PerimeterPosition

struct Slic3r::PreciseSeam::PerimeterPosition
Class Members
size_t edge_index
double parameter

◆ Slic3r::PreciseSeam::StrongSeamTarget

struct Slic3r::PreciseSeam::StrongSeamTarget
Collaboration diagram for Slic3r::PreciseSeam::StrongSeamTarget:
Class Members
size_t edge_index
Point point

◆ Slic3r::PreciseSeam::PerimeterSegment

struct Slic3r::PreciseSeam::PerimeterSegment
Collaboration diagram for Slic3r::PreciseSeam::PerimeterSegment:
Class Members
PerimeterPosition begin
vector< size_t > edge_indices
PerimeterPosition end
double length = 0.
Polyline polyline
optional< StrongSeamTarget > strong_target

◆ Slic3r::PreciseSeam::SegmentExtraction

struct Slic3r::PreciseSeam::SegmentExtraction
Collaboration diagram for Slic3r::PreciseSeam::SegmentExtraction:
Class Members
size_t discarded_fragments = 0
bool full_containment = false
vector< PerimeterSegment > segments
bool valid = true

◆ Slic3r::PreciseSeam::WeakModifierSegment

struct Slic3r::PreciseSeam::WeakModifierSegment
Collaboration diagram for Slic3r::PreciseSeam::WeakModifierSegment:
Class Members
Point left_point
PerimeterPosition left_position
const ModelVolume * modifier = nullptr
Point right_point
PerimeterPosition right_position
EnforcedBlockedSeamPoint type
bool whole_perimeter = false

Typedef Documentation

◆ ModifierRegions

◆ ModifierRegionsCache

◆ ModifierSlices

Enumeration Type Documentation

◆ EnforcedBlockedSeamPoint

38 {
39 Blocked = 0,
40 Neutral = 1,
41 Enforced = 2,
42};

Function Documentation

◆ apply_weak_modifiers_to_perimeter()

void Slic3r::PreciseSeam::apply_weak_modifiers_to_perimeter ( const std::vector< WeakModifierSegment > & weak_segments,
PrintObjectSeamData::LayerSeams & result,
const SeamPlacerImpl::Perimeter & perimeter,
bool & some_point_enforced,
PreciseSeamWarnings * warnings )
974{
975 // Get z-coordinate for unscaling boundary points
976 const float z_coord = result.points[perimeter.start_index].position.z();
977 const size_t perimeter_size = perimeter.end_index - perimeter.start_index;
978
979 // Helper lambda: search for point index in result.points by unscaled coordinates.
980 // Linear scan is intentional — O(N×M) is acceptable for typical M ≤ 5 weak segments.
981 auto find_point_index = [&](const Point &pt) -> std::optional<size_t> {
982 Vec2f unscaled_pt = unscale(pt).cast<float>();
983 Vec3f target(unscaled_pt.x(), unscaled_pt.y(), z_coord);
984 for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i) {
985 if (result.points[i].position == target) return i - perimeter.start_index;
986 }
987 return std::nullopt;
988 };
989
990 // Apply weak modifiers sequentially: sorted low-priority-first,
991 // so higher-priority modifiers (higher in object tree) overwrite via last-write-wins.
992 for (size_t seg_idx = 0; seg_idx < weak_segments.size(); ++seg_idx) {
993 const auto &segment = weak_segments[seg_idx];
994
995 // A whole-perimeter zone types every candidate, including painting's oversampled points.
996 if (segment.whole_perimeter) {
997 for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i)
998 result.points[i].type = segment.type;
999 if (segment.type == EnforcedBlockedSeamPoint::Enforced)
1000 some_point_enforced = true;
1001 continue;
1002 }
1003
1004 // Find boundary point indices in result.points
1005 std::optional<size_t> left_idx = find_point_index(segment.left_point);
1006 std::optional<size_t> right_idx = find_point_index(segment.right_point);
1007
1008 if (!left_idx.has_value() || !right_idx.has_value()) {
1009 BOOST_LOG_TRIVIAL(error) << "PreciseSeam: boundary point not found in perimeter, skipping segment";
1010 continue;
1011 }
1012
1013 // Apply modifier type to range [left_idx, right_idx] with wraparound
1014 for (size_t idx = left_idx.value(); ; idx = (idx + 1) % perimeter_size) {
1015 result.points[perimeter.start_index + idx].type = segment.type;
1016 if (idx == right_idx.value()) break;
1017 }
1018
1019 if (segment.type == EnforcedBlockedSeamPoint::Enforced) {
1020 some_point_enforced = true;
1021 }
1022 }
1023
1024 // Only the highest whole zone is in effect, and a higher band may still unblock part of it.
1025 if (warnings == nullptr)
1026 return;
1027 const auto last_whole = std::find_if(weak_segments.rbegin(), weak_segments.rend(),
1028 [](const WeakModifierSegment &segment) { return segment.whole_perimeter; });
1029 if (last_whole == weak_segments.rend() || last_whole->type != EnforcedBlockedSeamPoint::Blocked)
1030 return;
1031 for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i)
1032 if (result.points[i].type != EnforcedBlockedSeamPoint::Blocked)
1033 return;
1034 if (const auto usage = warnings->modifier_usage.find(last_whole->modifier); usage != warnings->modifier_usage.end())
1035 PreciseSeamWarnings::set(usage->second.fully_blocked);
1036}
Definition Point.hpp:199
void segment(CGALMesh &src, std::vector< CGALMesh > &dst, double smoothing_alpha=0.5, int segment_number=5)
Definition MeshBoolean.cpp:480
Definition PreciseSeam.hpp:143
size_t end_index
Definition SeamPlacer.hpp:50
size_t start_index
Definition SeamPlacer.hpp:49
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65
T unscale(Q v)
Definition libslic3r.h:138
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:64
static void set(std::atomic< bool > &flag)
Definition PreciseSeam.hpp:82
std::unordered_map< const ModelVolume *, ModifierUsage > modifier_usage
Definition PreciseSeam.hpp:67
std::vector< SeamPlacerImpl::SeamCandidate > points
Definition SeamPlacer.hpp:115

References Slic3r::SeamPlacerImpl::Perimeter::end_index, Slic3r::PreciseSeam::PreciseSeamWarnings::modifier_usage, Slic3r::PrintObjectSeamData::LayerSeams::points, Slic3r::PreciseSeam::PreciseSeamWarnings::set(), Slic3r::SeamPlacerImpl::Perimeter::start_index, and Slic3r::unscale().

Referenced by Slic3r::SeamPlacerImpl::process_perimeter_polygon().

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

◆ collect_weak_modifier_segments()

std::vector< WeakModifierSegment > Slic3r::PreciseSeam::collect_weak_modifier_segments ( const std::vector< const ModelVolume * > & weak_volumes,
Polygon & polygon,
const PreparedPerimeter & prepared,
const Layer * layer,
const ModifierRegionsCache & slices_cache,
PreciseSeamWarnings * warnings )
936{
937 assert(&prepared.polygon == &polygon);
938 std::vector<WeakModifierSegment> result;
939 if (weak_volumes.empty() || layer == nullptr || !prepared.valid)
940 return result;
941
942 // Modifier cache indices exclude raft layers, unlike Layer::id().
943 const size_t raft_layers = layer->object()->slicing_parameters().raft_layers();
944 const size_t layer_id = layer->id() - raft_layers;
945 for (const ModelVolume *modifier_volume : weak_volumes) {
946 const auto it = slices_cache.find(modifier_volume);
947 if (it == slices_cache.end() || layer_id >= it->second.size())
948 continue;
949 const ExtractionContext context{layer, modifier_volume, warnings};
950 const SegmentExtraction extracted = extract_perimeter_segments(prepared, it->second[layer_id], modifier_volume->type(), context);
951 record_extraction(warnings, modifier_volume, extracted);
952 const auto type = convert_weak_modifier_type(modifier_volume->type());
953 // Full containment works like seam painting all round: the whole perimeter takes the type.
954 // A perimeter left whole Blocked also gets a warning (see apply_weak_modifiers_to_perimeter()).
955 if (extracted.full_containment) {
956 result.push_back({type, Point(), PerimeterPosition{0, 0.}, Point(), PerimeterPosition{0, 0.}, true, modifier_volume});
957 continue;
958 }
959 for (const PerimeterSegment &segment : extracted.segments) {
960 result.push_back({type, segment.polyline.points.front(), segment.begin,
961 segment.polyline.points.back(), segment.end, false, modifier_volume});
962 }
963 }
964 // Boundary insertion, refinement and last-write-wins priority use the established path.
965 return prepare_weak_modifier_segments(std::move(result), polygon);
966}
size_t id() const
Definition Layer.hpp:141
PrintObject * object()
Definition Layer.hpp:143
Definition Model.hpp:832
const SlicingParameters & slicing_parameters() const
Definition Print.hpp:496
SegmentExtraction extract_perimeter_segments(const PreparedPerimeter &prepared, const ModifierRegions &modifier, ModelVolumeType mode, const ExtractionContext &context)
Definition PreciseSeam.cpp:408
static std::vector< WeakModifierSegment > prepare_weak_modifier_segments(std::vector< WeakModifierSegment > result, Polygon &polygon)
Definition PreciseSeam.cpp:780
static EnforcedBlockedSeamPoint convert_weak_modifier_type(ModelVolumeType type)
Definition PreciseSeam.cpp:754
static void record_extraction(PreciseSeamWarnings *warnings, const ModelVolume *modifier, const SegmentExtraction &extracted)
Definition PreciseSeam.cpp:678
Definition PreciseSeam.hpp:118
Definition PreciseSeam.hpp:90
const Polygon & polygon
Definition PreciseSeam.hpp:98
bool valid
Definition PreciseSeam.hpp:101
size_t raft_layers() const
Definition Slicing.hpp:44

References convert_weak_modifier_type(), extract_perimeter_segments(), Slic3r::PreciseSeam::SegmentExtraction::full_containment, Slic3r::Layer::id(), Slic3r::Layer::object(), Slic3r::PreciseSeam::PreparedPerimeter::polygon, prepare_weak_modifier_segments(), Slic3r::SlicingParameters::raft_layers(), record_extraction(), Slic3r::PreciseSeam::SegmentExtraction::segments, Slic3r::PrintObject::slicing_parameters(), and Slic3r::PreciseSeam::PreparedPerimeter::valid.

Referenced by Slic3r::SeamPlacerImpl::process_perimeter_polygon().

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

◆ convert_weak_modifier_type()

EnforcedBlockedSeamPoint Slic3r::PreciseSeam::convert_weak_modifier_type ( ModelVolumeType type)
static
754 {
755 assert(is_precise_seam_weak(type));
756 switch (type) {
758 return EnforcedBlockedSeamPoint::Enforced;
760 return EnforcedBlockedSeamPoint::Blocked;
762 return EnforcedBlockedSeamPoint::Neutral;
763 // Non-weak types are unreachable by precondition; listed to keep the switch exhaustive.
773 break;
774 }
775 assert(false && "convert_weak_modifier_type called with non-weak type");
776 return EnforcedBlockedSeamPoint::Neutral;
777}
bool is_precise_seam_weak(ModelVolumeType t)
Definition Model.hpp:375
@ PARAMETER_MODIFIER
Definition Model.hpp:359
@ PRECISE_SEAM_BLOCKED
Definition Model.hpp:368
@ PRECISE_SEAM_LEFT
Definition Model.hpp:365
@ MODEL_PART
Definition Model.hpp:357
@ SUPPORT_BLOCKER
Definition Model.hpp:360
@ PRECISE_SEAM_ENFORCED
Definition Model.hpp:367
@ INVALID
Definition Model.hpp:356
@ PRECISE_SEAM_CENTER
Definition Model.hpp:364
@ NEGATIVE_VOLUME
Definition Model.hpp:358
@ PRECISE_SEAM_NEUTRAL
Definition Model.hpp:369
@ PRECISE_SEAM_RIGHT
Definition Model.hpp:366
@ SUPPORT_ENFORCER
Definition Model.hpp:361

References Slic3r::INVALID, Slic3r::is_precise_seam_weak(), Slic3r::MODEL_PART, Slic3r::NEGATIVE_VOLUME, Slic3r::PARAMETER_MODIFIER, Slic3r::PRECISE_SEAM_BLOCKED, Slic3r::PRECISE_SEAM_CENTER, Slic3r::PRECISE_SEAM_ENFORCED, Slic3r::PRECISE_SEAM_LEFT, Slic3r::PRECISE_SEAM_NEUTRAL, Slic3r::PRECISE_SEAM_RIGHT, Slic3r::SUPPORT_BLOCKER, and Slic3r::SUPPORT_ENFORCER.

Referenced by collect_weak_modifier_segments().

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

◆ extract_perimeter_segments()

SegmentExtraction Slic3r::PreciseSeam::extract_perimeter_segments ( const PreparedPerimeter & prepared,
const ModifierRegions & modifier,
ModelVolumeType mode,
const ExtractionContext & context )
410{
412 SegmentExtraction result;
413 if (!prepared.valid) {
414 result.valid = false;
415 return result;
416 }
417 const Polygon &perimeter = prepared.polygon;
418 const size_t count = perimeter.size();
419 std::vector<const ExPolygon*> nearby_regions;
420 for (const ModifierRegion &region : modifier)
421 if (!region.polygon.empty() && prepared.bounds.overlap(region.bounds))
422 nearby_regions.push_back(&region.polygon);
423 if (nearby_regions.empty())
424 return result;
425
426 // Clip as an open line, without offsets or contact rules: modifiers should cross the perimeter
427 // clearly, and the open path keeps self-touching vertices apart.
428 std::vector<ClippedEdgeInterval> intervals;
429 Polylines fragments;
430 // Clip only accepted regions, with their holes still attached to the exterior.
431 for (const ExPolygon *region : nearby_regions) {
432 Polylines clipped = intersection_pl(prepared.line, *region);
433 for (Polyline &fragment : clipped)
434 // Clipper returns a single point where a border only touches the line: nothing to bind.
435 if (fragment.size() >= 2)
436 fragments.push_back(std::move(fragment));
437 }
438 for (size_t i = 0; i < fragments.size(); ++i)
439 if (!detail::append_fragment(fragments[i], perimeter, intervals, context, i))
440 ++result.discarded_fragments;
441
442 std::sort(intervals.begin(), intervals.end(), [](const auto &a, const auto &b) {
443 if (a.edge != b.edge) return a.edge < b.edge;
444 if (a.begin != b.begin) return a.begin < b.begin;
445 return a.end < b.end;
446 });
447 // Union overlaps on the same occurrence of an edge, never across equal coordinates.
448 std::vector<ClippedEdgeInterval> merged;
449 for (const ClippedEdgeInterval &interval : intervals) {
450 // Fragments touching on an edge meet at the same integer point. Comparing that point as well keeps
451 // them joined even if its two projections differ by an ulp on some platforms (e.g. FMA contraction).
452 if (!merged.empty() && merged.back().edge == interval.edge &&
453 (interval.begin <= merged.back().end || interval.first == merged.back().last)) {
454 if (interval.end > merged.back().end) {
455 merged.back().end = interval.end;
456 merged.back().last = interval.last;
457 }
458 } else
459 merged.push_back(interval);
460 }
461 const auto position = [count](size_t edge, double parameter) {
462 return parameter == 1. ? PerimeterPosition{(edge + 1) % count, 0.}
463 : PerimeterPosition{edge, parameter};
464 };
465 const auto same_position = [](const PerimeterPosition &a, const PerimeterPosition &b) {
466 return a.edge_index == b.edge_index && a.parameter == b.parameter;
467 };
468 for (const ClippedEdgeInterval &interval : merged) {
469 const auto begin = position(interval.edge, interval.begin);
470 const auto end = position(interval.edge, interval.end);
471 if (result.segments.empty() || !same_position(result.segments.back().end, begin)) {
473 segment.begin = begin;
474 segment.polyline.points.push_back(interval.first);
475 result.segments.push_back(std::move(segment));
476 }
477 PerimeterSegment &segment = result.segments.back();
478 segment.polyline.points.push_back(interval.last);
479 segment.edge_indices.push_back(interval.edge);
480 segment.end = end;
481 }
482 if (result.segments.size() > 1 && same_position(result.segments.back().end, result.segments.front().begin)) {
483 // Only the artificial cut at vertex zero can join the last and first intervals.
484 PerimeterSegment tail = std::move(result.segments.back());
485 result.segments.pop_back();
486 PerimeterSegment &head = result.segments.front();
487 tail.polyline.points.insert(tail.polyline.points.end(), head.polyline.points.begin() + 1, head.polyline.points.end());
488 tail.edge_indices.insert(tail.edge_indices.end(), head.edge_indices.begin(), head.edge_indices.end());
489 tail.end = head.end;
490 head = std::move(tail);
491 }
492 // A full loop must cover every original edge, not merely have equal endpoint coordinates.
493 result.full_containment = merged.size() == count;
494 for (size_t i = 0; result.full_containment && i < count; ++i)
495 result.full_containment = merged[i].edge == i && merged[i].begin == 0. && merged[i].end == 1.;
496 // A touch on an inclined edge leaves a sub-micron gap. Treat the single segment as full containment
497 // when the gap is shorter than the snapping radius, or spans one vertex with both ends within it
498 // (exact for edges of 2 um and longer). The 2 um end distance is a cheap pre-filter.
499 if (!result.full_containment && result.segments.size() == 1) {
500 const PerimeterSegment &only = result.segments.front();
501 const Point &first = only.polyline.points.front();
502 const Point &last = only.polyline.points.back();
503 const double limit = double(TOLERANCE_LINEAR);
504 if ((first - last).cast<double>().squaredNorm() < 4. * limit * limit) {
505 const auto edge_length = [&perimeter, count](size_t edge) {
506 return (perimeter[(edge + 1) % count] - perimeter[edge]).cast<double>().norm();
507 };
508 // Uncovered length from the segment's end forward to its begin; stops early once it exceeds the limit.
509 const auto gap_length = [&](const PerimeterPosition &from, const PerimeterPosition &to) {
510 if (from.edge_index == to.edge_index && from.parameter <= to.parameter)
511 return (to.parameter - from.parameter) * edge_length(from.edge_index);
512 double gap = (1. - from.parameter) * edge_length(from.edge_index);
513 for (size_t edge = (from.edge_index + 1) % count; gap < limit && edge != to.edge_index; edge = (edge + 1) % count)
514 gap += edge_length(edge);
515 return gap + to.parameter * edge_length(to.edge_index);
516 };
517 // Strict comparisons match the vertex snapping in insert_point_into_perimeter.
518 const auto within_snap = [limit](const Point &a, const Point &b) {
519 return (a - b).cast<double>().squaredNorm() < limit * limit;
520 };
521 const Point &vertex = perimeter[only.begin.edge_index];
522 const bool around_one_vertex = (only.end.edge_index + 1) % count == only.begin.edge_index &&
523 within_snap(last, vertex) && within_snap(first, vertex);
524 if (around_one_vertex || gap_length(only.end, only.begin) < limit)
525 result.full_containment = true;
526 }
527 }
528 // Strong skips full containment and weak handles it by type; neither needs strong data for it.
529 if (!result.full_containment && is_precise_seam_strong(mode))
530 for (PerimeterSegment &segment : result.segments)
531 prepare_strong_segment(segment, mode, result.segments.size() > 1);
532 return result;
533}
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
bool overlap(const BoundingBoxBase< PointType, PointsType > &other) const
Definition BoundingBox.hpp:58
Definition ExPolygon.hpp:23
auto begin()
Definition MultiPoint.hpp:114
size_t size() const
Definition MultiPoint.hpp:50
Definition Polygon.hpp:31
Definition Polyline.hpp:25
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
bool append_fragment(const Polyline &fragment, const Polygon &perimeter, std::vector< ClippedEdgeInterval > &intervals, const ExtractionContext &context, size_t fragment_index)
Definition PreciseSeam.cpp:321
Definition PreciseSeamInternal.hpp:13
static void prepare_strong_segment(PerimeterSegment &segment, ModelVolumeType mode, bool compare_lengths)
Definition PreciseSeam.cpp:369
bool full_containment
Definition PreciseSeam.hpp:131
static constexpr coord_t TOLERANCE_LINEAR
Definition PreciseSeam.cpp:74
std::vector< PerimeterSegment > segments
Definition PreciseSeam.hpp:130
size_t discarded_fragments
Definition PreciseSeam.hpp:134
bool valid
Definition PreciseSeam.hpp:132
Definition PreciseSeam.hpp:129
Definition PreciseSeam.hpp:107
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
bool is_precise_seam_strong(ModelVolumeType t)
Definition Model.hpp:374
static double edge_length(const VD::edge_type &edge)
Definition MultiMaterialSegmentation.cpp:1115
std::vector< Polyline > Polylines
Definition Polyline.hpp:22
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
Definition ClipperUtils.cpp:1111
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299
BoundingBox bounds
Definition PreciseSeam.hpp:99
Polyline line
Definition PreciseSeam.hpp:100

References Slic3r::PreciseSeam::detail::append_fragment(), Slic3r::PreciseSeam::PerimeterSegment::begin, Slic3r::PreciseSeam::PreparedPerimeter::bounds, Slic3r::cast(), count, Slic3r::PreciseSeam::SegmentExtraction::discarded_fragments, Slic3r::PreciseSeam::PerimeterPosition::edge_index, Slic3r::PreciseSeam::PerimeterSegment::edge_indices, Slic3r::edge_length(), Slic3r::PreciseSeam::PerimeterSegment::end, Slic3r::PreciseSeam::SegmentExtraction::full_containment, Slic3r::intersection_pl(), Slic3r::is_precise_seam_strong(), Slic3r::PreciseSeam::PreparedPerimeter::line, Slic3r::BoundingBoxBase< PointType, APointsType >::overlap(), Slic3r::PreciseSeam::PerimeterPosition::parameter, Slic3r::MultiPoint::points, Slic3r::PreciseSeam::PreparedPerimeter::polygon, Slic3r::PreciseSeam::PerimeterSegment::polyline, prepare_strong_segment(), Slic3r::PreciseSeam::SegmentExtraction::segments, Slic3r::MultiPoint::size(), TOLERANCE_LINEAR, Slic3r::PreciseSeam::PreparedPerimeter::valid, and Slic3r::PreciseSeam::SegmentExtraction::valid.

Referenced by collect_weak_modifier_segments(), and insert_strong_seam_point().

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

◆ init_precise_seam_data()

void Slic3r::PreciseSeam::init_precise_seam_data ( std::vector< const ModelVolume * > & strong_volumes_out,
std::vector< const ModelVolume * > & weak_volumes_out,
bool & has_strong_out,
const ModelObject * model_object )
540{
541 // Clear output vectors
542 strong_volumes_out.clear();
543 weak_volumes_out.clear();
544
545 if (model_object == nullptr) {
546 has_strong_out = false;
547 return;
548 }
549
550 // Collect and categorize precise seam modifiers
551 for (const ModelVolume* volume : model_object->volumes) {
552 if (volume->is_precise_seam()) {
553 // Strong (Center/Left/Right) and weak (Enforced/Blocked/Neutral) go to separate lists.
554 if (volume->is_precise_seam_strong())
555 strong_volumes_out.push_back(volume);
556 else
557 weak_volumes_out.push_back(volume);
558 }
559 }
560
561 has_strong_out = !strong_volumes_out.empty();
562
563 // Collection already preserves model order, with higher-priority strong modifiers first.
564 // Weak modifiers use last-write-wins, so apply the higher-priority ones last.
565 std::reverse(weak_volumes_out.begin(), weak_volumes_out.end());
566}
ModelVolumePtrs volumes
Definition Model.hpp:393
bool is_precise_seam_strong() const
Definition Model.hpp:1035
bool is_precise_seam() const
Definition Model.hpp:1032

References Slic3r::ModelVolume::is_precise_seam(), Slic3r::ModelVolume::is_precise_seam_strong(), and Slic3r::ModelObject::volumes.

Referenced by Slic3r::SeamPlacer::init().

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

◆ insert_point_into_perimeter()

std::pair< Point, size_t > Slic3r::PreciseSeam::insert_point_into_perimeter ( const Point & point,
size_t edge_start_idx,
Polygon & perimeter_polygon )
static
575{
576 assert(perimeter_polygon.points.size() >= 3);
577 size_t perim_max = perimeter_polygon.points.size();
578
579 // Determine edge start and end
580 size_t vtx_start = edge_start_idx;
581 size_t vtx_end = (edge_start_idx + 1) % perim_max;
582
583 const Point &perim_p_start = perimeter_polygon.points[vtx_start];
584 const Point &perim_p_end = perimeter_polygon.points[vtx_end];
585
586 // Check proximity to edge vertices
587 coord_t dist_sq_start = (point - perim_p_start).squaredNorm();
588 if (dist_sq_start < TOLERANCE_SQUARED) {
589 return std::make_pair(perim_p_start, vtx_start);
590 }
591
592 coord_t dist_sq_end = (point - perim_p_end).squaredNorm();
593 if (dist_sq_end < TOLERANCE_SQUARED) {
594 return std::make_pair(perim_p_end, vtx_end);
595 }
596
597 // On the closing edge, append instead of inserting at index 0: callers insert in descending source
598 // order and rely on earlier indices staying valid.
599 size_t insert_pos;
600 if (edge_start_idx == perim_max - 1) {
601 // Last edge: add to end of vector
602 perimeter_polygon.points.push_back(point);
603 insert_pos = perimeter_polygon.points.size() - 1;
604 } else {
605 // Regular edge: insert before end vertex
606 insert_pos = vtx_end;
607 perimeter_polygon.points.insert(
608 perimeter_polygon.points.begin() + insert_pos,
609 point
610 );
611 }
612
613 return std::make_pair(perimeter_polygon.points[insert_pos], insert_pos);
614}
Points points
Definition MultiPoint.hpp:22
int64_t coord_t
Definition libslic3r.h:48
static constexpr coord_t TOLERANCE_SQUARED
Definition PreciseSeam.cpp:75

References Slic3r::MultiPoint::points, and TOLERANCE_SQUARED.

Referenced by insert_strong_seam_point(), and prepare_weak_modifier_segments().

Here is the caller graph for this function:

◆ insert_strong_seam_point()

std::optional< Point > Slic3r::PreciseSeam::insert_strong_seam_point ( const std::vector< const ModelVolume * > & strong_volumes,
Polygon & polygon,
const PreparedPerimeter & prepared,
const Layer * layer,
const ModifierRegionsCache & slices_cache,
PreciseSeamWarnings * warnings )
702{
703 assert(&prepared.polygon == &polygon);
704 if (strong_volumes.empty() || layer == nullptr || !prepared.valid)
705 return std::nullopt;
706 const size_t layer_id = layer->id() - layer->object()->slicing_parameters().raft_layers();
707 for (const ModelVolume *modifier : strong_volumes) {
708 const auto it = slices_cache.find(modifier);
709 if (it == slices_cache.end() || layer_id >= it->second.size())
710 continue;
712 prepared, it->second[layer_id], modifier->type(), {layer, modifier, warnings});
713 record_extraction(warnings, modifier, extracted);
714 // Full containment leaves no intersection to place the point on: skipped with a warning.
715 if (extracted.full_containment) {
716 if (warnings)
717 PreciseSeamWarnings::mark(warnings->full_containment, modifier->type());
718 continue;
719 }
720 // No usable segment, even if all fragments were discarded: pass the turn to the next modifier.
721 if (extracted.segments.empty())
722 continue;
723 if (warnings && extracted.segments.size() > 1)
724 PreciseSeamWarnings::mark(warnings->multiple_intersections, modifier->type());
725
726 // Lengths are compared exactly: for a symmetric modifier the chosen face may alternate between
727 // layers, which is accepted (the user gets the multiple-intersections warning).
728 StrongSeamTarget target{Point(0, 0), 0};
729 double longest = -1.;
730 for (const PerimeterSegment &segment : extracted.segments) {
731 assert(segment.strong_target.has_value());
732 const StrongSeamTarget &candidate = *segment.strong_target;
733 // Slice coordinates already include object rotation and retain bed axes.
734 // Centering/instance translation cannot change rear (+Y), then left (-X) ordering.
735 const bool farther_or_left = candidate.point.y() > target.point.y() ||
736 (candidate.point.y() == target.point.y() && candidate.point.x() < target.point.x());
737 if (segment.length > longest || (segment.length == longest && farther_or_left)) {
738 longest = segment.length;
739 target = candidate;
740 }
741 }
742 const auto [seam_point, seam_index] = insert_point_into_perimeter(target.point, target.edge_index, polygon);
743 // Preserve the insertion order: refining before first would shift the seam index.
744 refine_at_vertex(seam_index, +1, polygon);
745 refine_at_vertex(seam_index, -1, polygon);
746 return seam_point; // No later strong modifier or weak processing for this perimeter.
747 }
748 return std::nullopt;
749}
static void refine_at_vertex(size_t point_idx, int direction, Polygon &perimeter_polygon)
Definition PreciseSeam.cpp:618
static std::pair< Point, size_t > insert_point_into_perimeter(const Point &point, size_t edge_start_idx, Polygon &perimeter_polygon)
Definition PreciseSeam.cpp:570
static void mark(std::atomic< unsigned > &mask, ModelVolumeType type)
Definition PreciseSeam.hpp:76
std::atomic< unsigned > full_containment
Definition PreciseSeam.hpp:52
std::atomic< unsigned > multiple_intersections
Definition PreciseSeam.hpp:51

References Slic3r::PreciseSeam::StrongSeamTarget::edge_index, extract_perimeter_segments(), Slic3r::PreciseSeam::PreciseSeamWarnings::full_containment, Slic3r::PreciseSeam::SegmentExtraction::full_containment, Slic3r::Layer::id(), insert_point_into_perimeter(), Slic3r::PreciseSeam::PreciseSeamWarnings::mark(), Slic3r::PreciseSeam::PreciseSeamWarnings::multiple_intersections, Slic3r::Layer::object(), Slic3r::PreciseSeam::StrongSeamTarget::point, Slic3r::PreciseSeam::PreparedPerimeter::polygon, Slic3r::SlicingParameters::raft_layers(), record_extraction(), refine_at_vertex(), Slic3r::PreciseSeam::SegmentExtraction::segments, Slic3r::PrintObject::slicing_parameters(), and Slic3r::PreciseSeam::PreparedPerimeter::valid.

Referenced by Slic3r::SeamPlacerImpl::process_perimeter_polygon().

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

◆ prepare_modifier_regions()

ModifierRegions Slic3r::PreciseSeam::prepare_modifier_regions ( ExPolygons regions)
30{
31 ModifierRegions cached;
32 cached.reserve(regions.size());
33 // Each exterior is measured once; its holes remain in the same cached region.
34 for (ExPolygon &region : regions)
35 cached.emplace_back(std::move(region));
36 return cached;
37}
std::vector< ModifierRegion > ModifierRegions
Definition PreciseSeam.hpp:34

Referenced by prepare_modifier_slices().

Here is the caller graph for this function:

◆ prepare_modifier_slices()

ModifierSlices Slic3r::PreciseSeam::prepare_modifier_slices ( std::vector< ExPolygons > slices)
40{
41 ModifierSlices cached;
42 cached.reserve(slices.size());
43 // Keep empty layers so callers can index by the original object layer number.
44 for (ExPolygons &layer : slices)
45 cached.push_back(prepare_modifier_regions(std::move(layer)));
46 return cached;
47}
std::vector< ModifierRegions > ModifierSlices
Definition PreciseSeam.hpp:35
ModifierRegions prepare_modifier_regions(ExPolygons regions)
Definition PreciseSeam.cpp:29
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:20

References prepare_modifier_regions().

Referenced by Slic3r::SeamPlacer::init().

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

◆ prepare_strong_segment()

void Slic3r::PreciseSeam::prepare_strong_segment ( PerimeterSegment & segment,
ModelVolumeType mode,
bool compare_lengths )
static
370{
371 const bool center = mode == ModelVolumeType::PRECISE_SEAM_CENTER;
372 std::vector<double> edge_lengths;
373 if (center)
374 edge_lengths.reserve(segment.edge_indices.size());
375 // A single Left/Right segment needs only its endpoint; Center always needs arc length.
376 if (center || compare_lengths) {
377 for (size_t i = 0; i < segment.edge_indices.size(); ++i) {
378 const double length = (segment.polyline.points[i + 1].cast<double>() -
379 segment.polyline.points[i].cast<double>()).norm();
380 segment.length += length;
381 if (center)
382 edge_lengths.push_back(length);
383 }
384 }
386 segment.strong_target = StrongSeamTarget{segment.polyline.points.front(), segment.begin.edge_index};
387 return;
388 }
389 segment.strong_target = StrongSeamTarget{segment.polyline.points.back(), segment.end.edge_index};
390 if (!center)
391 return;
392
393 // The total is now known; reuse local lengths to locate its half without new square roots.
394 double remaining = segment.length * 0.5;
395 for (size_t i = 0; i < edge_lengths.size(); ++i) {
396 const double length = edge_lengths[i];
397 if (remaining <= length && length > 0.) {
398 const Point &a = segment.polyline.points[i];
399 const Point &b = segment.polyline.points[i + 1];
400 segment.strong_target = StrongSeamTarget{
401 a + ((remaining / length) * (b - a).cast<double>()).cast<coord_t>(), segment.edge_indices[i]};
402 return;
403 }
404 remaining -= length;
405 }
406}
Definition PreciseSeam.hpp:113
double length(const Points &pts)
Definition MultiPoint.hpp:181
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382

References Slic3r::cast(), Slic3r::center(), Slic3r::length(), Slic3r::PRECISE_SEAM_CENTER, and Slic3r::PRECISE_SEAM_LEFT.

Referenced by extract_perimeter_segments().

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

◆ prepare_weak_modifier_segments()

std::vector< WeakModifierSegment > Slic3r::PreciseSeam::prepare_weak_modifier_segments ( std::vector< WeakModifierSegment > result,
Polygon & polygon )
static
782{
783 // If no segments, return empty vector
784 if (result.empty()) {
785 return result;
786 }
787
788 // Source positions have the same order as arc length, without measuring the perimeter.
789 struct PointToInsert {
790 size_t segment_idx;
791 bool is_left;
792 PerimeterPosition position;
793 };
794 std::vector<PointToInsert> points_to_insert;
795 points_to_insert.reserve(result.size() * 2);
796 for (size_t seg_idx = 0; seg_idx < result.size(); ++seg_idx) {
797 const WeakModifierSegment &seg = result[seg_idx];
798 // A whole-perimeter zone has no boundaries to insert.
799 if (seg.whole_perimeter)
800 continue;
801 points_to_insert.push_back({seg_idx, true, seg.left_position});
802 points_to_insert.push_back({seg_idx, false, seg.right_position});
803 }
804
805 // Descending order preserves pending source indices; vertex zero is canonical (0, 0).
806 // Only insertion events are reordered: modifier priority in result remains unchanged.
807 std::sort(points_to_insert.begin(), points_to_insert.end(),
808 [](const PointToInsert &a, const PointToInsert &b) {
809 if (a.position.edge_index != b.position.edge_index)
810 return a.position.edge_index > b.position.edge_index;
811 return a.position.parameter > b.position.parameter;
812 });
813
814 for (const PointToInsert &pt : points_to_insert) {
815 WeakModifierSegment &seg = result[pt.segment_idx];
816 Point &point_coords = pt.is_left ? seg.left_point : seg.right_point;
817 // Take the final coordinates: a boundary close to an existing vertex snaps to it.
818 point_coords = insert_point_into_perimeter(point_coords, pt.position.edge_index, polygon).first;
819 }
820
821 // Add helpers at boundary vertices, scanning original vertices backwards so insertions do not shift
822 // pending indices.
823 for (size_t poly_idx = polygon.size(); poly_idx-- > 0; ) {
824 bool refine_before = false;
825 bool refine_after = false;
826 for (const WeakModifierSegment &segment : result) {
827 // Its placeholder points must not match a real vertex.
828 if (segment.whole_perimeter)
829 continue;
830 refine_before |= polygon[poly_idx] == segment.left_point;
831 refine_after |= polygon[poly_idx] == segment.right_point;
832 }
833 // Insert after first: inserting before would shift the current vertex index.
834 if (refine_after)
835 refine_at_vertex(poly_idx, +1, polygon);
836 if (refine_before)
837 refine_at_vertex(poly_idx, -1, polygon);
838 }
839
840 // Split edges in enforced zones into segments ≤ enforcer_oversampling_distance
841 // Determine type pattern for each polygon edge (sequential application of hierarchy)
842 std::vector<EnforcedBlockedSeamPoint> edge_types(polygon.size(), EnforcedBlockedSeamPoint::Neutral);
843
844 // Every boundary was inserted or snapped above and later steps only add vertices, so it is found.
845 // A linear scan is fine: there are only a few zones per perimeter.
846 auto find_point_index = [&](const Point &pt) -> size_t {
847 const auto it = std::find(polygon.points.begin(), polygon.points.end(), pt);
848 assert(it != polygon.points.end());
849 return size_t(it - polygon.points.begin());
850 };
851
852 // Last write wins: zones are sorted lowest priority first.
853 for (const auto &segment : result) {
854 // A whole-perimeter zone types every edge, so Enforced subdivides the whole perimeter, as
855 // painting it green all round would.
856 if (segment.whole_perimeter) {
857 std::fill(edge_types.begin(), edge_types.end(), segment.type);
858 continue;
859 }
860 const size_t left_idx = find_point_index(segment.left_point);
861 const size_t right_idx = find_point_index(segment.right_point);
862 // Practically unreachable, but a miss would write past edge_types in Release builds: skip the zone.
863 if (left_idx == polygon.size() || right_idx == polygon.size()) {
864 BOOST_LOG_TRIVIAL(error) << "PreciseSeam: weak boundary not found in perimeter, skipping zone";
865 continue;
866 }
867
868 // A zone covers edges [left_idx, right_idx): the edge from the right boundary is outside and must
869 // not be subdivided. Edge types only drive subdivision; candidates are typed per point later.
870 for (size_t idx = left_idx; idx != right_idx; idx = (idx + 1) % polygon.size())
871 edge_types[idx] = segment.type;
872 }
873
874 // Boundaries and helpers are already in place; without enforced edges nothing is subdivided,
875 // so skip rebuilding the polygon.
876 if (std::find(edge_types.begin(), edge_types.end(), EnforcedBlockedSeamPoint::Enforced) == edge_types.end())
877 return result;
878
879 // Split enforced edges into small segments
880 const double STEP_SCALED = scale_(SeamPlacer::enforcer_oversampling_distance);
881
882 // Collect new list of polygon points with split enforced edges
883 Points new_points;
884 new_points.reserve(polygon.size() * 10); // Approximate estimate
885
886 for (size_t i = 0; i < polygon.size(); ++i) {
887 size_t next_i = (i + 1) % polygon.size();
888 Point p_start = polygon[i];
889 Point p_end = polygon[next_i];
890
891 // Add current vertex
892 new_points.push_back(p_start);
893
894 // Check edge type [i, next_i]
895 if (edge_types[i] != EnforcedBlockedSeamPoint::Enforced) {
896 continue; // Not enforced - don't split
897 }
898
899 // Calculate subdivision parameters
900 Vec2d edge_vec = (p_end - p_start).cast<double>();
901 double edge_length = edge_vec.norm();
902
903 if (edge_length <= STEP_SCALED) {
904 continue; // Edge too short - don't split
905 }
906
907 // Number of segments: ceil(L / S)
908 size_t num_segments = static_cast<size_t>(std::ceil(edge_length / STEP_SCALED));
909
910 // Uniform step: L / num_segments (each segment ≤ S)
911 double actual_step = edge_length / num_segments;
912 Vec2d step_vec = edge_vec.normalized() * actual_step;
913
914 // Add intermediate points incrementally
915 Vec2d current_pos = p_start.cast<double>();
916 for (size_t j = 1; j < num_segments; ++j) {
917 current_pos += step_vec;
918 new_points.push_back(current_pos.cast<coord_t>());
919 }
920 }
921
922 // Replace polygon points with new ones (with split enforced edges)
923 polygon.points = std::move(new_points);
924
925 return result;
926}
static constexpr float enforcer_oversampling_distance
Definition SeamPlacer.hpp:150
#define scale_(val)
Definition libslic3r.h:106
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67

References Slic3r::cast(), Slic3r::edge_length(), Slic3r::SeamPlacer::enforcer_oversampling_distance, insert_point_into_perimeter(), Slic3r::PreciseSeam::WeakModifierSegment::left_point, Slic3r::PreciseSeam::WeakModifierSegment::left_position, Slic3r::MultiPoint::points, refine_at_vertex(), Slic3r::PreciseSeam::WeakModifierSegment::right_point, Slic3r::PreciseSeam::WeakModifierSegment::right_position, scale_, Slic3r::MultiPoint::size(), and Slic3r::PreciseSeam::WeakModifierSegment::whole_perimeter.

Referenced by collect_weak_modifier_segments().

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

◆ record_extraction()

void Slic3r::PreciseSeam::record_extraction ( PreciseSeamWarnings * warnings,
const ModelVolume * modifier,
const SegmentExtraction & extracted )
static
680{
681 if (warnings == nullptr || !extracted.valid)
682 return;
683 if (extracted.discarded_fragments > 0)
684 PreciseSeamWarnings::mark(warnings->failed_types, modifier->type());
685 const auto it = warnings->modifier_usage.find(modifier);
686 if (it == warnings->modifier_usage.end())
687 return;
688 PreciseSeamWarnings::ModifierUsage &usage = it->second;
690 if (!extracted.segments.empty() || extracted.full_containment || extracted.discarded_fragments > 0)
692}
ModelVolumeType type() const
Definition Model.hpp:1023
std::atomic< unsigned > failed_types
Definition PreciseSeam.hpp:53
std::atomic< bool > checked
Definition PreciseSeam.hpp:63
std::atomic< bool > reached
Definition PreciseSeam.hpp:64

References Slic3r::PreciseSeam::PreciseSeamWarnings::ModifierUsage::checked, Slic3r::PreciseSeam::SegmentExtraction::discarded_fragments, Slic3r::PreciseSeam::PreciseSeamWarnings::failed_types, Slic3r::PreciseSeam::SegmentExtraction::full_containment, Slic3r::PreciseSeam::PreciseSeamWarnings::mark(), Slic3r::PreciseSeam::PreciseSeamWarnings::modifier_usage, Slic3r::PreciseSeam::PreciseSeamWarnings::ModifierUsage::reached, Slic3r::PreciseSeam::SegmentExtraction::segments, Slic3r::PreciseSeam::PreciseSeamWarnings::set(), Slic3r::ModelVolume::type(), and Slic3r::PreciseSeam::SegmentExtraction::valid.

Referenced by collect_weak_modifier_segments(), and insert_strong_seam_point().

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

◆ refine_at_vertex()

void Slic3r::PreciseSeam::refine_at_vertex ( size_t point_idx,
int direction,
Polygon & perimeter_polygon )
static
623{
624 assert(perimeter_polygon.points.size() >= 3);
625 assert(direction == 1 || direction == -1);
626
627 size_t perim_max = perimeter_polygon.points.size();
628
629 // Determine target edge based on direction
630 size_t edge_start_idx, edge_end_idx;
631
632 if (direction == 1) {
633 // Direction +1: insertion AFTER point_idx (edge point_idx → point_idx+1)
634 edge_start_idx = point_idx;
635 edge_end_idx = (point_idx + 1) % perim_max;
636 } else {
637 // Direction -1: insertion BEFORE point_idx (edge point_idx-1 → point_idx)
638 edge_start_idx = (point_idx + perim_max - 1) % perim_max;
639 edge_end_idx = point_idx;
640 }
641
642 const Point &edge_start = perimeter_polygon.points[edge_start_idx];
643 const Point &edge_end = perimeter_polygon.points[edge_end_idx];
644
645 // Calculate edge length
646 Vec2d edge_vector = (edge_end - edge_start).cast<double>();
647 double edge_length = edge_vector.norm();
648
649 // The helper needs TOLERANCE_LINEAR of edge on both sides.
650 if (edge_length < 2.0 * TOLERANCE_LINEAR) {
651 return; // Edge too short - new point would be too close to end
652 }
653
654 // Calculate new point coordinates: edge_start + TOLERANCE_LINEAR * direction_normalized
655 Vec2d direction_normalized = edge_vector / edge_length;
656 // Place helper point near point_idx: after it for +1, before it for -1
657 auto offset = (TOLERANCE_LINEAR * direction_normalized).cast<coord_t>();
658 Point new_point = (direction == 1)
659 ? Point(edge_start + offset)
660 : Point(edge_end - offset);
661
662 // On the closing edge, append instead of inserting at index 0: callers insert in descending source
663 // order and rely on earlier indices staying valid.
664 if (edge_start_idx == perim_max - 1) {
665 // Last edge: add to end of vector
666 perimeter_polygon.points.push_back(new_point);
667 } else {
668 // Regular edge: insert before end vertex
669 perimeter_polygon.points.insert(
670 perimeter_polygon.points.begin() + edge_end_idx,
671 new_point
672 );
673 }
674}
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582

References Slic3r::cast(), Slic3r::edge_length(), Slic3r::offset(), Slic3r::MultiPoint::points, and TOLERANCE_LINEAR.

Referenced by insert_strong_seam_point(), and prepare_weak_modifier_segments().

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

◆ restore_precise_seam_positions()

void Slic3r::PreciseSeam::restore_precise_seam_positions ( std::vector< PrintObjectSeamData::LayerSeams > & layers)
1039 {
1042
1043 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
1044 [&layers](tbb::blocked_range<size_t> r) {
1045 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1046 std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
1047 // Iterate over perimeters (jump by end_index)
1048 for (size_t current = 0; current < layer_perimeter_points.size();
1049 current = layer_perimeter_points[current].perimeter.end_index) {
1050 Perimeter &perimeter = layer_perimeter_points[current].perimeter;
1051 if (perimeter.precise_seam_point.has_value()) {
1052 perimeter.final_seam_position = perimeter.precise_seam_point.value();
1053 perimeter.seam_index = perimeter.precise_seam_index;
1054 }
1055 }
1056 }
1057 });
1058}
Definition SeamPlacer.hpp:45
Definition SeamPlacer.hpp:72

Referenced by Slic3r::SeamPlacer::init().

Here is the caller graph for this function:

Variable Documentation

◆ failed_fragment_log_limit

size_t Slic3r::PreciseSeam::failed_fragment_log_limit = 10
inlineconstexpr

◆ MACHINE_PRECISION_SQUARED

double Slic3r::PreciseSeam::MACHINE_PRECISION_SQUARED = 2.5
staticconstexpr

◆ TOLERANCE_LINEAR

coord_t Slic3r::PreciseSeam::TOLERANCE_LINEAR = 1000
staticconstexpr

◆ TOLERANCE_SQUARED