Improve performance of top layer line recoloring
This commit is contained in:
@@ -33,6 +33,32 @@ struct PerimeterPathBoundarySample {
|
||||
float inset_mm { 0.f };
|
||||
};
|
||||
|
||||
struct PerimeterTextureMaskIndex {
|
||||
const ExPolygons *expolygons { nullptr };
|
||||
std::vector<BoundingBox> bboxes;
|
||||
BoundingBox bbox;
|
||||
|
||||
bool empty() const { return expolygons == nullptr || expolygons->empty() || bboxes.empty() || !bbox.defined; }
|
||||
};
|
||||
|
||||
static ExPolygons perimeter_texture_expolygons_overlapping_bbox(const ExPolygons &expolygons,
|
||||
const std::vector<BoundingBox> *bboxes,
|
||||
const BoundingBox &bbox)
|
||||
{
|
||||
ExPolygons out;
|
||||
if (expolygons.empty() || !bbox.defined)
|
||||
return out;
|
||||
|
||||
for (size_t idx = 0; idx < expolygons.size(); ++idx) {
|
||||
BoundingBox expolygon_bbox = bboxes != nullptr && idx < bboxes->size() ?
|
||||
(*bboxes)[idx] :
|
||||
get_extents(expolygons[idx]);
|
||||
if (expolygon_bbox.defined && expolygon_bbox.overlap(bbox))
|
||||
out.emplace_back(expolygons[idx]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void perimeter_texture_collect_external_widths(const ExtrusionEntity &entity, std::vector<float> &widths)
|
||||
{
|
||||
if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath *>(&entity)) {
|
||||
@@ -103,7 +129,16 @@ static ExPolygons perimeter_texture_external_visible_footprint(const LayerRegion
|
||||
if (layer == nullptr || layer->upper_layer == nullptr || layer->upper_layer->lslices.empty())
|
||||
return footprint;
|
||||
|
||||
ExPolygons visible = diff_ex(footprint, layer->upper_layer->lslices);
|
||||
const BoundingBox footprint_bbox = get_extents(footprint);
|
||||
const ExPolygons upper_slices = footprint_bbox.defined ?
|
||||
perimeter_texture_expolygons_overlapping_bbox(layer->upper_layer->lslices,
|
||||
&layer->upper_layer->lslices_bboxes,
|
||||
footprint_bbox) :
|
||||
layer->upper_layer->lslices;
|
||||
if (upper_slices.empty())
|
||||
return footprint;
|
||||
|
||||
ExPolygons visible = diff_ex(footprint, upper_slices);
|
||||
return visible.empty() ? footprint : visible;
|
||||
}
|
||||
|
||||
@@ -492,6 +527,28 @@ static std::optional<unsigned int> perimeter_texture_choose_recolor_component_wi
|
||||
sampler.gradient_contexts);
|
||||
}
|
||||
|
||||
static void perimeter_texture_move_reusable_offset_context(PerimeterTextureRecolorSampler &sampler,
|
||||
unsigned int active_component_id,
|
||||
std::optional<TextureMappingOffsetContext> *reusable_context)
|
||||
{
|
||||
if (reusable_context == nullptr)
|
||||
return;
|
||||
|
||||
reusable_context->reset();
|
||||
if (sampler.image_texture) {
|
||||
if (sampler.image_context)
|
||||
*reusable_context = std::move(*sampler.image_context);
|
||||
return;
|
||||
}
|
||||
|
||||
for (TextureMappingOffsetContext &context : sampler.gradient_contexts) {
|
||||
if (context.active_component_id == active_component_id) {
|
||||
*reusable_context = std::move(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<unsigned int> perimeter_texture_choose_recolor_component_for_visible(
|
||||
const LayerRegion &layer_region,
|
||||
const ExPolygons &visible,
|
||||
@@ -597,10 +654,20 @@ static ExPolygons perimeter_texture_top_visible_wall_band_mask(const LayerRegion
|
||||
continue;
|
||||
|
||||
ExPolygons visible;
|
||||
if (layer->upper_layer != nullptr && !layer->upper_layer->lslices.empty())
|
||||
visible = diff_ex(surface.expolygon, layer->upper_layer->lslices);
|
||||
else
|
||||
if (layer->upper_layer != nullptr && !layer->upper_layer->lslices.empty()) {
|
||||
const BoundingBox surface_bbox = get_extents(surface.expolygon);
|
||||
const ExPolygons upper_slices = surface_bbox.defined ?
|
||||
perimeter_texture_expolygons_overlapping_bbox(layer->upper_layer->lslices,
|
||||
&layer->upper_layer->lslices_bboxes,
|
||||
surface_bbox) :
|
||||
layer->upper_layer->lslices;
|
||||
if (upper_slices.empty())
|
||||
visible.emplace_back(surface.expolygon);
|
||||
else
|
||||
visible = diff_ex(surface.expolygon, upper_slices);
|
||||
} else {
|
||||
visible.emplace_back(surface.expolygon);
|
||||
}
|
||||
if (visible.empty())
|
||||
continue;
|
||||
|
||||
@@ -622,13 +689,80 @@ static ExPolygons perimeter_texture_top_visible_wall_band_mask(const LayerRegion
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool perimeter_texture_expolygons_contain_point(const ExPolygons &expolygons, const Point &point);
|
||||
static PerimeterTextureMaskIndex perimeter_texture_make_mask_index(const ExPolygons *mask)
|
||||
{
|
||||
PerimeterTextureMaskIndex out;
|
||||
if (mask == nullptr || mask->empty())
|
||||
return out;
|
||||
|
||||
static bool perimeter_texture_sample_is_top_visible_recolor_protected(const ExPolygons *mask,
|
||||
const PerimeterPathBoundarySample &sample,
|
||||
double tangent_x,
|
||||
double tangent_y,
|
||||
float max_inset_mm,
|
||||
out.expolygons = mask;
|
||||
out.bboxes.reserve(mask->size());
|
||||
for (const ExPolygon &expolygon : *mask) {
|
||||
BoundingBox bbox = get_extents(expolygon);
|
||||
out.bboxes.emplace_back(bbox);
|
||||
if (bbox.defined)
|
||||
out.bbox.merge(bbox);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool perimeter_texture_mask_index_contains_point(const PerimeterTextureMaskIndex &mask, const Point &point)
|
||||
{
|
||||
if (mask.empty() || !mask.bbox.contains(point))
|
||||
return false;
|
||||
|
||||
for (size_t idx = 0; idx < mask.expolygons->size() && idx < mask.bboxes.size(); ++idx) {
|
||||
if (mask.bboxes[idx].defined &&
|
||||
mask.bboxes[idx].contains(point) &&
|
||||
(*mask.expolygons)[idx].contains(point, true))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ExPolygons perimeter_texture_mask_index_overlapping_expolygons(const PerimeterTextureMaskIndex &mask,
|
||||
const BoundingBox &bbox)
|
||||
{
|
||||
ExPolygons out;
|
||||
if (mask.empty() || !bbox.defined || !mask.bbox.overlap(bbox))
|
||||
return out;
|
||||
|
||||
for (size_t idx = 0; idx < mask.expolygons->size() && idx < mask.bboxes.size(); ++idx) {
|
||||
if (mask.bboxes[idx].defined && mask.bboxes[idx].overlap(bbox))
|
||||
out.emplace_back((*mask.expolygons)[idx]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static Polygons perimeter_texture_mask_index_clipped_polygons(const PerimeterTextureMaskIndex &mask,
|
||||
const BoundingBox &bbox)
|
||||
{
|
||||
Polygons out;
|
||||
if (mask.empty() || !bbox.defined || !mask.bbox.overlap(bbox))
|
||||
return out;
|
||||
|
||||
const BoundingBox clip_bbox = bbox.inflated(SCALED_EPSILON);
|
||||
for (size_t idx = 0; idx < mask.expolygons->size() && idx < mask.bboxes.size(); ++idx) {
|
||||
if (!mask.bboxes[idx].defined || !mask.bboxes[idx].overlap(bbox))
|
||||
continue;
|
||||
Polygons clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox((*mask.expolygons)[idx], clip_bbox);
|
||||
polygons_append(out, std::move(clipped));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool perimeter_texture_expolygons_contain_point(const ExPolygons &expolygons, const Point &point)
|
||||
{
|
||||
return std::any_of(expolygons.begin(), expolygons.end(), [&point](const ExPolygon &expolygon) {
|
||||
return expolygon.contains(point, true);
|
||||
});
|
||||
}
|
||||
|
||||
static bool perimeter_texture_sample_is_top_visible_recolor_protected(const PerimeterTextureMaskIndex *mask,
|
||||
const PerimeterPathBoundarySample &sample,
|
||||
double tangent_x,
|
||||
double tangent_y,
|
||||
float max_inset_mm,
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *thresholds)
|
||||
{
|
||||
if (mask == nullptr || mask->empty() || thresholds == nullptr)
|
||||
@@ -638,7 +772,7 @@ static bool perimeter_texture_sample_is_top_visible_recolor_protected(const ExPo
|
||||
const double inset_scaled = scale_(double(inset_mm));
|
||||
const Point candidate(coord_t(std::llround(double(sample.point.x()) + sample.inward_x * inset_scaled)),
|
||||
coord_t(std::llround(double(sample.point.y()) + sample.inward_y * inset_scaled)));
|
||||
return perimeter_texture_expolygons_contain_point(*mask, candidate);
|
||||
return perimeter_texture_mask_index_contains_point(*mask, candidate);
|
||||
};
|
||||
|
||||
if (!contains_at_inset(0.f) &&
|
||||
@@ -662,34 +796,27 @@ static bool perimeter_texture_sample_is_top_visible_recolor_protected(const ExPo
|
||||
if (!footprint.is_valid())
|
||||
return false;
|
||||
|
||||
ExPolygons footprint_ex = union_ex(Polygons{ footprint });
|
||||
if (footprint_ex.empty())
|
||||
return false;
|
||||
|
||||
const double footprint_area_mm2 = perimeter_texture_scaled_area_mm2(area(footprint_ex));
|
||||
const double footprint_area_mm2 = perimeter_texture_scaled_area_mm2(footprint.area());
|
||||
if (footprint_area_mm2 <= EPSILON)
|
||||
return false;
|
||||
|
||||
ExPolygons visible = intersection_ex(footprint_ex, *mask);
|
||||
Polygons local_mask = perimeter_texture_mask_index_clipped_polygons(*mask, get_extents(footprint));
|
||||
if (local_mask.empty())
|
||||
return false;
|
||||
|
||||
Polygons visible = intersection(Polygons{ footprint }, local_mask);
|
||||
const double visible_area_mm2 = visible.empty() ? 0.0 : perimeter_texture_scaled_area_mm2(area(visible));
|
||||
return visible_area_mm2 / footprint_area_mm2 >= thresholds->visible_fraction;
|
||||
}
|
||||
|
||||
static bool perimeter_texture_expolygons_contain_point(const ExPolygons &expolygons, const Point &point)
|
||||
{
|
||||
return std::any_of(expolygons.begin(), expolygons.end(), [&point](const ExPolygon &expolygon) {
|
||||
return expolygon.contains(point, true);
|
||||
});
|
||||
}
|
||||
|
||||
static ExPolygons perimeter_texture_segment_visible_footprint(float depth,
|
||||
const Point &a,
|
||||
const Point &b,
|
||||
const ExPolygons &top_visible_mask,
|
||||
double &footprint_area_mm2)
|
||||
static Polygons perimeter_texture_segment_visible_footprint(float depth,
|
||||
const Point &a,
|
||||
const Point &b,
|
||||
const PerimeterTextureMaskIndex &top_visible_mask,
|
||||
double &footprint_area_mm2)
|
||||
{
|
||||
footprint_area_mm2 = 0.0;
|
||||
if (a == b || !std::isfinite(depth) || depth <= 0.f)
|
||||
if (a == b || top_visible_mask.empty() || !std::isfinite(depth) || depth <= 0.f)
|
||||
return {};
|
||||
|
||||
const double dx = double(b.x()) - double(a.x());
|
||||
@@ -713,49 +840,51 @@ static ExPolygons perimeter_texture_segment_visible_footprint(float
|
||||
if (!footprint.is_valid())
|
||||
return {};
|
||||
|
||||
ExPolygons footprint_ex = union_ex(Polygons{ footprint });
|
||||
if (footprint_ex.empty())
|
||||
return {};
|
||||
|
||||
footprint_area_mm2 = perimeter_texture_scaled_area_mm2(area(footprint_ex));
|
||||
footprint_area_mm2 = perimeter_texture_scaled_area_mm2(footprint.area());
|
||||
if (footprint_area_mm2 <= EPSILON)
|
||||
return {};
|
||||
|
||||
ExPolygons footprint_visible = intersection_ex(footprint_ex, top_visible_mask);
|
||||
Polygons local_mask = perimeter_texture_mask_index_clipped_polygons(top_visible_mask, get_extents(footprint));
|
||||
if (local_mask.empty())
|
||||
return {};
|
||||
|
||||
Polygons footprint_visible = intersection(Polygons{ footprint }, local_mask);
|
||||
if (footprint_visible.empty())
|
||||
return {};
|
||||
|
||||
return union_ex(footprint_visible);
|
||||
return footprint_visible;
|
||||
}
|
||||
|
||||
static void perimeter_texture_append_colored_line(ColoredLines &lines, const Line &line, int color)
|
||||
{
|
||||
if (line.a == line.b)
|
||||
return;
|
||||
if (!lines.empty() && lines.back().color == color && lines.back().line.b == line.a) {
|
||||
const Point &a = lines.back().line.a;
|
||||
const Point &b = lines.back().line.b;
|
||||
const Point &c = line.b;
|
||||
if (int128::orient(a, b, c) == 0) {
|
||||
lines.back().line.b = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
lines.emplace_back(ColoredLine{ line, color });
|
||||
}
|
||||
|
||||
static ColoredLines perimeter_texture_colored_lines_for_polygon(const LayerRegion &layer_region,
|
||||
const Polygon &polygon,
|
||||
const ExPolygons &top_visible_mask,
|
||||
const TextureMappingZone &zone,
|
||||
unsigned int texture_zone_id,
|
||||
static ColoredLines perimeter_texture_colored_lines_for_polygon(const Polygon &polygon,
|
||||
const PerimeterTextureMaskIndex &top_visible_mask,
|
||||
float base_width_mm,
|
||||
const PerimeterTextureTopVisibleRecolorThresholds &thresholds)
|
||||
const PerimeterTextureTopVisibleRecolorThresholds &thresholds,
|
||||
const PerimeterTextureRecolorSampler &recolor_sampler)
|
||||
{
|
||||
if (polygon.points.size() < 3)
|
||||
return {};
|
||||
|
||||
std::optional<PerimeterTextureRecolorSampler> recolor_sampler =
|
||||
perimeter_texture_make_recolor_sampler(layer_region, zone, texture_zone_id, base_width_mm);
|
||||
if (!recolor_sampler)
|
||||
if (polygon.points.size() < 3 || top_visible_mask.empty())
|
||||
return {};
|
||||
|
||||
struct Segment {
|
||||
Line line;
|
||||
int color { 0 };
|
||||
bool eligible { false };
|
||||
ExPolygons visible;
|
||||
Polygons visible;
|
||||
double visible_area_mm2 { 0.0 };
|
||||
};
|
||||
|
||||
@@ -763,7 +892,7 @@ static ColoredLines perimeter_texture_colored_lines_for_polygon(const LayerRegio
|
||||
size_t start { 0 };
|
||||
size_t end { 0 };
|
||||
double length_scaled { 0.0 };
|
||||
ExPolygons visible;
|
||||
Polygons visible;
|
||||
double visible_area_mm2 { 0.0 };
|
||||
};
|
||||
|
||||
@@ -788,12 +917,15 @@ static ColoredLines perimeter_texture_colored_lines_for_polygon(const LayerRegio
|
||||
const Point b(coord_t(std::llround(double(start.x()) + dx * t1)),
|
||||
coord_t(std::llround(double(start.y()) + dy * t1)));
|
||||
double footprint_area_mm2 = 0.0;
|
||||
ExPolygons visible = perimeter_texture_segment_visible_footprint(base_width_mm, a, b, top_visible_mask, footprint_area_mm2);
|
||||
Polygons visible = perimeter_texture_segment_visible_footprint(base_width_mm, a, b, top_visible_mask, footprint_area_mm2);
|
||||
const double visible_area_mm2 = visible.empty() ? 0.0 : perimeter_texture_scaled_area_mm2(area(visible));
|
||||
const double visible_fraction = footprint_area_mm2 > EPSILON ? visible_area_mm2 / footprint_area_mm2 : 0.0;
|
||||
const bool eligible = visible_fraction >= thresholds.visible_fraction && visible_area_mm2 > EPSILON;
|
||||
if (a != b)
|
||||
if (a != b) {
|
||||
if (!eligible)
|
||||
visible.clear();
|
||||
segments.push_back(Segment{ Line(a, b), 0, eligible, std::move(visible), visible_area_mm2 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,7 +985,7 @@ static ColoredLines perimeter_texture_colored_lines_for_polygon(const LayerRegio
|
||||
for (size_t gap_idx = run.end; gap_idx < idx; ++gap_idx)
|
||||
run.length_scaled += segment_length(gap_idx);
|
||||
run.length_scaled += segment_length(idx);
|
||||
append(run.visible, segments[idx].visible);
|
||||
append(run.visible, std::move(segments[idx].visible));
|
||||
run.visible_area_mm2 += segments[idx].visible_area_mm2;
|
||||
run.end = idx + 1;
|
||||
pending_gap_scaled = 0.0;
|
||||
@@ -884,7 +1016,7 @@ static ColoredLines perimeter_texture_colored_lines_for_polygon(const LayerRegio
|
||||
std::optional<unsigned int> component_id =
|
||||
perimeter_texture_choose_recolor_component_with_sampler(visible,
|
||||
nullptr,
|
||||
*recolor_sampler);
|
||||
recolor_sampler);
|
||||
if (component_id && *component_id > 0)
|
||||
set_run_color(run, *component_id);
|
||||
}
|
||||
@@ -892,6 +1024,15 @@ static ColoredLines perimeter_texture_colored_lines_for_polygon(const LayerRegio
|
||||
ColoredLines out;
|
||||
for (const Segment &segment : segments)
|
||||
perimeter_texture_append_colored_line(out, segment.line, segment.color);
|
||||
if (out.size() > 1 && out.front().color == out.back().color && out.back().line.b == out.front().line.a) {
|
||||
const Point &a = out.back().line.a;
|
||||
const Point &b = out.back().line.b;
|
||||
const Point &c = out.front().line.b;
|
||||
if (int128::orient(a, b, c) == 0) {
|
||||
out.front().line.a = a;
|
||||
out.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -901,10 +1042,9 @@ static std::vector<ExPolygons> perimeter_texture_top_visible_region_recolor_mask
|
||||
const SurfaceCollection &slices,
|
||||
const ExPolygons &wall_band,
|
||||
const ExPolygons &top_visible_mask,
|
||||
const TextureMappingZone &zone,
|
||||
unsigned int texture_zone_id,
|
||||
float base_width_mm,
|
||||
const PerimeterTextureTopVisibleRecolorThresholds &thresholds)
|
||||
const PerimeterTextureTopVisibleRecolorThresholds &thresholds,
|
||||
const PerimeterTextureRecolorSampler &recolor_sampler)
|
||||
{
|
||||
const Layer *layer = layer_region.layer();
|
||||
const size_t num_physical = layer != nullptr && layer->object() != nullptr && layer->object()->print() != nullptr ?
|
||||
@@ -913,34 +1053,51 @@ static std::vector<ExPolygons> perimeter_texture_top_visible_region_recolor_mask
|
||||
std::vector<ExPolygons> out(num_physical + 1);
|
||||
if (num_physical == 0 || slices.empty() || wall_band.empty() || top_visible_mask.empty())
|
||||
return out;
|
||||
const PerimeterTextureMaskIndex wall_band_index =
|
||||
perimeter_texture_make_mask_index(&wall_band);
|
||||
const PerimeterTextureMaskIndex top_visible_mask_index =
|
||||
perimeter_texture_make_mask_index(&top_visible_mask);
|
||||
if (wall_band_index.empty() || top_visible_mask_index.empty())
|
||||
return out;
|
||||
|
||||
for (const Surface &surface : slices.surfaces) {
|
||||
const ExPolygon &expolygon = surface.expolygon;
|
||||
if (expolygon.empty())
|
||||
continue;
|
||||
const BoundingBox island_bbox = get_extents(expolygon);
|
||||
ExPolygons local_wall_band = perimeter_texture_mask_index_overlapping_expolygons(wall_band_index, island_bbox);
|
||||
if (local_wall_band.empty())
|
||||
continue;
|
||||
ExPolygons island_source{ expolygon };
|
||||
ExPolygons island_wall_band = intersection_ex(island_source, wall_band);
|
||||
ExPolygons island_wall_band = intersection_ex(island_source, local_wall_band);
|
||||
if (island_wall_band.empty())
|
||||
continue;
|
||||
ExPolygons local_top_visible_mask =
|
||||
perimeter_texture_mask_index_overlapping_expolygons(top_visible_mask_index, get_extents(island_wall_band));
|
||||
if (local_top_visible_mask.empty())
|
||||
continue;
|
||||
ExPolygons island_top_visible_mask = intersection_ex(island_wall_band, local_top_visible_mask);
|
||||
if (island_top_visible_mask.empty())
|
||||
continue;
|
||||
const PerimeterTextureMaskIndex island_top_visible_mask_index =
|
||||
perimeter_texture_make_mask_index(&island_top_visible_mask);
|
||||
if (island_top_visible_mask_index.empty())
|
||||
continue;
|
||||
|
||||
std::vector<ColoredLines> colored_contours;
|
||||
ColoredLines contour = perimeter_texture_colored_lines_for_polygon(layer_region,
|
||||
expolygon.contour,
|
||||
top_visible_mask,
|
||||
zone,
|
||||
texture_zone_id,
|
||||
ColoredLines contour = perimeter_texture_colored_lines_for_polygon(expolygon.contour,
|
||||
island_top_visible_mask_index,
|
||||
base_width_mm,
|
||||
thresholds);
|
||||
thresholds,
|
||||
recolor_sampler);
|
||||
if (!contour.empty())
|
||||
colored_contours.emplace_back(std::move(contour));
|
||||
for (const Polygon &hole : expolygon.holes) {
|
||||
ColoredLines hole_lines = perimeter_texture_colored_lines_for_polygon(layer_region,
|
||||
hole,
|
||||
top_visible_mask,
|
||||
zone,
|
||||
texture_zone_id,
|
||||
ColoredLines hole_lines = perimeter_texture_colored_lines_for_polygon(hole,
|
||||
island_top_visible_mask_index,
|
||||
base_width_mm,
|
||||
thresholds);
|
||||
thresholds,
|
||||
recolor_sampler);
|
||||
if (!hole_lines.empty())
|
||||
colored_contours.emplace_back(std::move(hole_lines));
|
||||
}
|
||||
@@ -994,10 +1151,13 @@ static void perimeter_texture_top_visible_recolor_data(const LayerRegion &
|
||||
float texture_external_width_mm,
|
||||
std::vector<ExPolygons> &top_visible_recolor_masks,
|
||||
ExPolygons &top_visible_recolor_protection_mask,
|
||||
PerimeterTextureTopVisibleRecolorThresholds &top_visible_recolor_thresholds)
|
||||
PerimeterTextureTopVisibleRecolorThresholds &top_visible_recolor_thresholds,
|
||||
std::optional<TextureMappingOffsetContext> *reusable_offset_context = nullptr)
|
||||
{
|
||||
top_visible_recolor_masks.clear();
|
||||
top_visible_recolor_protection_mask.clear();
|
||||
if (reusable_offset_context != nullptr)
|
||||
reusable_offset_context->reset();
|
||||
top_visible_recolor_thresholds =
|
||||
perimeter_texture_top_visible_recolor_thresholds(zone.top_visible_perimeter_recolor_aggressiveness);
|
||||
const PrintRegionConfig ®ion_config = layer_region.region().config();
|
||||
@@ -1013,15 +1173,26 @@ static void perimeter_texture_top_visible_recolor_data(const LayerRegion &
|
||||
if (top_visible_recolor_mask.empty())
|
||||
return;
|
||||
|
||||
std::optional<PerimeterTextureRecolorSampler> recolor_sampler =
|
||||
perimeter_texture_make_recolor_sampler(layer_region, zone, texture_zone_id, texture_external_width_mm);
|
||||
if (!recolor_sampler)
|
||||
return;
|
||||
|
||||
top_visible_recolor_masks =
|
||||
perimeter_texture_top_visible_region_recolor_masks(layer_region,
|
||||
slices,
|
||||
top_visible_recolor_wall_band,
|
||||
top_visible_recolor_mask,
|
||||
zone,
|
||||
texture_zone_id,
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_thresholds);
|
||||
top_visible_recolor_thresholds,
|
||||
*recolor_sampler);
|
||||
const Layer *layer = layer_region.layer();
|
||||
const Print *print = layer != nullptr && layer->object() != nullptr ? layer->object()->print() : nullptr;
|
||||
const size_t num_physical = print != nullptr ? print->config().filament_colour.values.size() : 0;
|
||||
const unsigned int active_component_id = print != nullptr && num_physical > 0 ?
|
||||
print->texture_mapping_manager().resolve_zone_component(texture_zone_id, num_physical, int(layer->id())) :
|
||||
0;
|
||||
perimeter_texture_move_reusable_offset_context(*recolor_sampler, active_component_id, reusable_offset_context);
|
||||
if (!perimeter_texture_recolor_masks_have_color(top_visible_recolor_masks)) {
|
||||
top_visible_recolor_masks.clear();
|
||||
return;
|
||||
@@ -1044,12 +1215,19 @@ static bool perimeter_texture_split_surfaces_by_recolor_masks(const SurfaceColle
|
||||
|
||||
ExPolygons colored_union;
|
||||
colored.clear();
|
||||
std::vector<PerimeterTextureMaskIndex> mask_indexes(masks.size());
|
||||
for (size_t idx = 1; idx < masks.size(); ++idx)
|
||||
mask_indexes[idx] = perimeter_texture_make_mask_index(&masks[idx]);
|
||||
for (size_t idx = 1; idx < masks.size(); ++idx) {
|
||||
if (masks[idx].empty())
|
||||
if (mask_indexes[idx].empty())
|
||||
continue;
|
||||
SurfaceCollection collection;
|
||||
for (const Surface &surface : colored_input.surfaces) {
|
||||
ExPolygons clipped = intersection_ex(surface.expolygon, masks[idx]);
|
||||
ExPolygons local_mask =
|
||||
perimeter_texture_mask_index_overlapping_expolygons(mask_indexes[idx], get_extents(surface.expolygon));
|
||||
if (local_mask.empty())
|
||||
continue;
|
||||
ExPolygons clipped = intersection_ex(surface.expolygon, local_mask);
|
||||
if (!clipped.empty())
|
||||
collection.append(std::move(clipped), surface);
|
||||
}
|
||||
@@ -1143,7 +1321,9 @@ static std::vector<PerimeterPathBoundarySample> perimeter_texture_sample_polygon
|
||||
const Polygon &polygon,
|
||||
const TextureMappingOffsetContext &context,
|
||||
const ExPolygon &source,
|
||||
const ExPolygons *top_visible_recolor_mask = nullptr,
|
||||
const std::vector<ExPolygons> &erode_ladder,
|
||||
float erode_step_mm,
|
||||
const PerimeterTextureMaskIndex *top_visible_recolor_mask = nullptr,
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds = nullptr)
|
||||
{
|
||||
std::vector<PerimeterPathBoundarySample> samples;
|
||||
@@ -1151,9 +1331,6 @@ static std::vector<PerimeterPathBoundarySample> perimeter_texture_sample_polygon
|
||||
if (points.size() < 3)
|
||||
return samples;
|
||||
|
||||
float erode_step_mm = 0.f;
|
||||
const std::vector<ExPolygons> erode_ladder =
|
||||
perimeter_texture_build_erode_ladder(source, context.max_width_delta_mm, erode_step_mm);
|
||||
const double pitch_scaled = scale_(context.high_resolution_texture_sampling ? 0.08 : 0.16);
|
||||
|
||||
for (size_t idx = 0; idx < points.size(); ++idx) {
|
||||
@@ -1296,8 +1473,22 @@ static ExPolygons perimeter_texture_modulated_expolygon(const ExPolygon &source,
|
||||
if (!std::isfinite(source_area) || source_area <= 0.0)
|
||||
return {};
|
||||
|
||||
const PerimeterTextureMaskIndex protection_mask =
|
||||
perimeter_texture_make_mask_index(top_visible_recolor_mask);
|
||||
const PerimeterTextureMaskIndex *protection_mask_ptr =
|
||||
protection_mask.empty() ? nullptr : &protection_mask;
|
||||
float erode_step_mm = 0.f;
|
||||
const std::vector<ExPolygons> erode_ladder =
|
||||
perimeter_texture_build_erode_ladder(source, context.max_width_delta_mm, erode_step_mm);
|
||||
|
||||
const std::vector<PerimeterPathBoundarySample> contour_samples =
|
||||
perimeter_texture_sample_polygon_boundary(source.contour, context, source, top_visible_recolor_mask, top_visible_recolor_thresholds);
|
||||
perimeter_texture_sample_polygon_boundary(source.contour,
|
||||
context,
|
||||
source,
|
||||
erode_ladder,
|
||||
erode_step_mm,
|
||||
protection_mask_ptr,
|
||||
top_visible_recolor_thresholds);
|
||||
if (contour_samples.size() < 3)
|
||||
return {};
|
||||
|
||||
@@ -1311,7 +1502,13 @@ static ExPolygons perimeter_texture_modulated_expolygon(const ExPolygon &source,
|
||||
std::vector<std::vector<PerimeterPathBoundarySample>> hole_samples;
|
||||
hole_samples.reserve(source.holes.size());
|
||||
for (const Polygon &hole : source.holes) {
|
||||
hole_samples.emplace_back(perimeter_texture_sample_polygon_boundary(hole, context, source, top_visible_recolor_mask, top_visible_recolor_thresholds));
|
||||
hole_samples.emplace_back(perimeter_texture_sample_polygon_boundary(hole,
|
||||
context,
|
||||
source,
|
||||
erode_ladder,
|
||||
erode_step_mm,
|
||||
protection_mask_ptr,
|
||||
top_visible_recolor_thresholds));
|
||||
if (hole_samples.back().size() < 3)
|
||||
return {};
|
||||
for (const PerimeterPathBoundarySample &sample : hole_samples.back()) {
|
||||
@@ -1367,21 +1564,27 @@ static SurfaceCollection perimeter_path_modulated_surfaces(const LayerRegion
|
||||
unsigned int texture_zone_id,
|
||||
std::optional<float> base_outer_width_mm = std::nullopt,
|
||||
const ExPolygons *top_visible_recolor_mask = nullptr,
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds = nullptr)
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds = nullptr,
|
||||
const TextureMappingOffsetContext *prebuilt_context = nullptr)
|
||||
{
|
||||
const Layer *layer = layer_region.layer();
|
||||
if (layer == nullptr || layer->object() == nullptr)
|
||||
return slices;
|
||||
|
||||
std::optional<TextureMappingOffsetContext> context =
|
||||
build_texture_mapping_offset_context_for_layer(*layer->object(),
|
||||
*layer,
|
||||
zone,
|
||||
texture_zone_id,
|
||||
0,
|
||||
base_outer_width_mm);
|
||||
if (!context)
|
||||
return slices;
|
||||
std::optional<TextureMappingOffsetContext> built_context;
|
||||
const TextureMappingOffsetContext *context = prebuilt_context;
|
||||
if (context == nullptr) {
|
||||
built_context =
|
||||
build_texture_mapping_offset_context_for_layer(*layer->object(),
|
||||
*layer,
|
||||
zone,
|
||||
texture_zone_id,
|
||||
0,
|
||||
base_outer_width_mm);
|
||||
if (!built_context)
|
||||
return slices;
|
||||
context = &*built_context;
|
||||
}
|
||||
|
||||
SurfaceCollection out;
|
||||
out.surfaces.reserve(slices.surfaces.size());
|
||||
@@ -1491,6 +1694,7 @@ void Layer::apply_perimeter_path_modulation_v2()
|
||||
PerimeterTextureTopVisibleRecolorThresholds top_visible_recolor_thresholds;
|
||||
const ExPolygons *top_visible_recolor_mask_ptr = nullptr;
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds_ptr = nullptr;
|
||||
std::optional<TextureMappingOffsetContext> reusable_modulation_context;
|
||||
if (zone->recolor_top_visible_perimeter_sections) {
|
||||
const float texture_external_width_mm =
|
||||
std::max(0.05f, float(print_object->print()->config().texture_mapping_outer_wall_gradient_max_line_width.value));
|
||||
@@ -1501,7 +1705,8 @@ void Layer::apply_perimeter_path_modulation_v2()
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_masks,
|
||||
top_visible_recolor_protection_mask,
|
||||
top_visible_recolor_thresholds);
|
||||
top_visible_recolor_thresholds,
|
||||
&reusable_modulation_context);
|
||||
if (!top_visible_recolor_protection_mask.empty()) {
|
||||
top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask;
|
||||
top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds;
|
||||
@@ -1514,7 +1719,8 @@ void Layer::apply_perimeter_path_modulation_v2()
|
||||
texture_zone_id,
|
||||
std::nullopt,
|
||||
top_visible_recolor_mask_ptr,
|
||||
top_visible_recolor_thresholds_ptr);
|
||||
top_visible_recolor_thresholds_ptr,
|
||||
reusable_modulation_context ? &*reusable_modulation_context : nullptr);
|
||||
layerm->slices = std::move(modulated_slices);
|
||||
layerm->raw_slices = to_expolygons(layerm->slices.surfaces);
|
||||
layerm->perimeter_path_modulation_v2_applied = true;
|
||||
@@ -1602,6 +1808,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
const ExPolygons *top_visible_recolor_mask_ptr = nullptr;
|
||||
PerimeterTextureTopVisibleRecolorThresholds top_visible_recolor_thresholds;
|
||||
const PerimeterTextureTopVisibleRecolorThresholds *top_visible_recolor_thresholds_ptr = nullptr;
|
||||
std::optional<TextureMappingOffsetContext> reusable_modulation_context;
|
||||
unsigned int perimeter_texture_zone_id = 0;
|
||||
bool use_perimeter_path_modulation = false;
|
||||
bool use_legacy_perimeter_path_modulation = false;
|
||||
@@ -1617,7 +1824,8 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
texture_external_width_mm,
|
||||
top_visible_recolor_masks,
|
||||
top_visible_recolor_protection_mask,
|
||||
top_visible_recolor_thresholds);
|
||||
top_visible_recolor_thresholds,
|
||||
perimeter_path_zone->uses_legacy_perimeter_path_modulation() ? &reusable_modulation_context : nullptr);
|
||||
if (!top_visible_recolor_protection_mask.empty()) {
|
||||
top_visible_recolor_mask_ptr = &top_visible_recolor_protection_mask;
|
||||
top_visible_recolor_thresholds_ptr = &top_visible_recolor_thresholds;
|
||||
@@ -1630,7 +1838,8 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
perimeter_texture_zone_id,
|
||||
std::nullopt,
|
||||
top_visible_recolor_mask_ptr,
|
||||
top_visible_recolor_thresholds_ptr);
|
||||
top_visible_recolor_thresholds_ptr,
|
||||
reusable_modulation_context ? &*reusable_modulation_context : nullptr);
|
||||
perimeter_slices = &modulated_slices;
|
||||
use_legacy_perimeter_path_modulation = true;
|
||||
} else if (perimeter_path_zone->uses_perimeter_path_modulation_v2()) {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "AABBTreeLines.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <oneapi/tbb/blocked_range.h>
|
||||
#include <oneapi/tbb/concurrent_vector.h>
|
||||
@@ -500,6 +502,49 @@ void PrintObject::make_perimeters()
|
||||
m_print->set_status(15, L("Generating walls"));
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating walls..." << log_memory_info();
|
||||
|
||||
const size_t total_wall_layers = m_layers.size();
|
||||
const size_t wall_progress_step = std::max<size_t>(1, total_wall_layers / 100);
|
||||
tbb::spin_mutex wall_status_mutex;
|
||||
auto wall_progress_percent = [](int min_percent, int max_percent, size_t current, size_t total) {
|
||||
if (total == 0)
|
||||
return min_percent;
|
||||
const double progress = std::min(1.0, double(current) / double(total));
|
||||
return min_percent + int(std::floor(double(max_percent - min_percent) * progress));
|
||||
};
|
||||
auto set_wall_status = [&](int percent, const std::string &message) {
|
||||
tbb::spin_mutex::scoped_lock lock(wall_status_mutex);
|
||||
m_print->set_status(percent, message);
|
||||
};
|
||||
auto should_report_wall_layer = [wall_progress_step, total_wall_layers](size_t current) {
|
||||
return current == 1 || current == total_wall_layers || current % wall_progress_step == 0;
|
||||
};
|
||||
auto layer_region_needs_v2_wall_geometry_update = [this](const LayerRegion *layerm) {
|
||||
if (layerm == nullptr)
|
||||
return false;
|
||||
if (layerm->perimeter_path_modulation_v2_applied)
|
||||
return true;
|
||||
const int filament_id = layerm->region().config().wall_filament.value;
|
||||
if (filament_id <= 0)
|
||||
return false;
|
||||
const TextureMappingZone *zone = this->print()->texture_mapping_manager().zone_from_id(unsigned(filament_id));
|
||||
return zone != nullptr &&
|
||||
zone->enabled &&
|
||||
!zone->deleted &&
|
||||
zone->uses_perimeter_path_modulation_v2() &&
|
||||
(zone->is_2d_gradient() || zone->is_image_texture());
|
||||
};
|
||||
bool report_v2_wall_geometry_progress = false;
|
||||
for (const Layer *layer : m_layers) {
|
||||
for (const LayerRegion *layerm : layer->regions()) {
|
||||
if (layer_region_needs_v2_wall_geometry_update(layerm)) {
|
||||
report_v2_wall_geometry_progress = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (report_v2_wall_geometry_progress)
|
||||
break;
|
||||
}
|
||||
|
||||
// Revert the typed slices into untyped slices.
|
||||
if (m_typed_slices) {
|
||||
for (Layer *layer : m_layers) {
|
||||
@@ -509,9 +554,21 @@ void PrintObject::make_perimeters()
|
||||
m_typed_slices = false;
|
||||
}
|
||||
|
||||
size_t prepared_wall_layers = 0;
|
||||
for (auto layer_it = m_layers.rbegin(); layer_it != m_layers.rend(); ++layer_it) {
|
||||
m_print->throw_if_canceled();
|
||||
if (report_v2_wall_geometry_progress && should_report_wall_layer(prepared_wall_layers + 1))
|
||||
set_wall_status(wall_progress_percent(15, 18, prepared_wall_layers, total_wall_layers),
|
||||
Slic3r::format(L("Generating walls: preparing layer %1%/%2%"),
|
||||
prepared_wall_layers + 1,
|
||||
total_wall_layers));
|
||||
(*layer_it)->apply_perimeter_path_modulation_v2();
|
||||
++prepared_wall_layers;
|
||||
if (report_v2_wall_geometry_progress && prepared_wall_layers == total_wall_layers)
|
||||
set_wall_status(18,
|
||||
Slic3r::format(L("Generating walls: preparing layer %1%/%2%"),
|
||||
prepared_wall_layers,
|
||||
total_wall_layers));
|
||||
}
|
||||
|
||||
// compare each layer to the one below, and mark those slices needing
|
||||
@@ -586,12 +643,36 @@ void PrintObject::make_perimeters()
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
|
||||
if (total_wall_layers > 0)
|
||||
set_wall_status(19, Slic3r::format(L("Generating walls: layer %1%/%2%"), 0, total_wall_layers));
|
||||
std::atomic<size_t> completed_wall_layers { 0 };
|
||||
std::atomic<size_t> next_wall_progress_report { wall_progress_step };
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
[this,
|
||||
total_wall_layers,
|
||||
wall_progress_step,
|
||||
&completed_wall_layers,
|
||||
&next_wall_progress_report,
|
||||
&wall_progress_percent,
|
||||
&set_wall_status](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_layers[layer_idx]->make_perimeters();
|
||||
const size_t completed = ++completed_wall_layers;
|
||||
if (completed == total_wall_layers) {
|
||||
set_wall_status(24, Slic3r::format(L("Generating walls: layer %1%/%2%"), completed, total_wall_layers));
|
||||
} else {
|
||||
size_t next_report = next_wall_progress_report.load();
|
||||
while (completed >= next_report) {
|
||||
const size_t next_target = std::min(total_wall_layers, completed + wall_progress_step);
|
||||
if (next_wall_progress_report.compare_exchange_weak(next_report, next_target)) {
|
||||
set_wall_status(wall_progress_percent(19, 24, completed, total_wall_layers),
|
||||
Slic3r::format(L("Generating walls: layer %1%/%2%"), completed, total_wall_layers));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user