diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp
index 5e0f2b052..afec93be7 100644
--- a/src/libslic3r/CutSurface.cpp
+++ b/src/libslic3r/CutSurface.cpp
@@ -373,16 +373,18 @@ bool is_over_whole_expoly(const SurfacePatch &patch,
///
/// Contain loops and vertices
/// Know how to project from 3d to 2d
+/// Range of unprojected points x .. min, y .. max value
/// Unprojected points in loops
-Polygons unproject_loops(const SurfacePatch &patch, const Project &projection);
+Polygons unproject_loops(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range);
///
/// Unproject points from loops and create expolygons
///
-///
+/// Patch to convert on expolygon
/// Convert 3d point to 2d
-///
-ExPolygon to_expoly(const SurfacePatch &patch, const Project &projection);
+/// Range of unprojected points x .. min, y .. max value
+/// Expolygon represent patch in 2d
+ExPolygon to_expoly(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range);
///
/// To select surface near projection distance
@@ -3170,7 +3172,7 @@ std::vector priv::select_patches(const ProjectionDistances &best_distances
{
// extension to cover numerical mistake made by back projection patch from 3d to 2d
const float extend_delta = 5.f / Emboss::SHAPE_SCALE; // [Font points scaled by Emboss::SHAPE_SCALE]
-
+
// vector of patches for shape
std::vector> used_shapes_patches(shapes.size());
std::vector in_distances(patches.size(), {false});
@@ -3189,115 +3191,134 @@ std::vector priv::select_patches(const ProjectionDistances &best_distances
for (const SurfacePatch &patch : patches)
shapes_patches[patch.shape_id].push_back(&patch - &patches.front());
- for (size_t shape_index = 0; shape_index < shapes.size(); shape_index++)
- {
+ for (size_t shape_index = 0; shape_index < shapes.size(); shape_index++) {
const ExPolygon &shape = shapes[shape_index];
- const std::vector &used_shape_patches = used_shapes_patches[shape_index];
+ std::vector &used_shape_patches = used_shapes_patches[shape_index];
if (used_shape_patches.empty()) continue;
+ // is used all exist patches?
+ if (used_shapes_patches.size() == shapes_patches[shape_index].size()) continue;
if (used_shape_patches.size() == 1) {
uint32_t patch_index = used_shape_patches.front();
const SurfacePatch &patch = patches[patch_index];
if (is_over_whole_expoly(patch, shapes, cutAOIs, meshes)) continue;
}
- // only shapes contain multiple patches
- // or not full filled are hard processed
+ // only shapes containing multiple patches
+ // or not full filled are back projected (hard processed)
- // convert patch to 2d
+ // intersection of converted patches to 2d
ExPolygons fill;
fill.reserve(used_shape_patches.size());
- for (uint32_t patch_index : used_shape_patches) {
- ExPolygon patch_area = to_expoly(patches[patch_index], projection);
+
+ // Heuristics to predict which patch to be used need average patch depth
+ Vec2d used_patches_depth(std::numeric_limits::max(), std::numeric_limits::min());
+ for (uint32_t patch_index : used_shape_patches) {
+ ExPolygon patch_area = to_expoly(patches[patch_index], projection, used_patches_depth);
//*/
ExPolygons patch_areas = offset_ex(patch_area, extend_delta);
- fill.insert(fill.end(), patch_areas.begin(), patch_areas.end());
+ fill.insert(fill.end(), patch_areas.begin(), patch_areas.end());
/*/
// without save extension
fill.push_back(patch_area);
//*/
}
+ fill = union_ex(fill);
+
+ // not cutted area of expolygon
ExPolygons rest = diff_ex(ExPolygons{shape}, fill, ApplySafetyOffset::Yes);
+#ifdef DEBUG_OUTPUT_DIR
+ SVG svg(DEBUG_OUTPUT_DIR + "input_patches_" + std::to_string(shape_index) + ".svg");
+ svg.draw(fill, "darkgreen");
+ svg.draw(rest, "green");
+#endif // DEBUG_OUTPUT_DIR
+
+ // already filled by multiple patches
if (rest.empty()) continue;
- // find patches overlaed rest area
- fill = union_ex(fill);
+ // find patches overlaped rest area
struct PatchShape{
uint32_t patch_index;
ExPolygon shape;
ExPolygons intersection;
+ double depth_range_center_distance; // always positive
};
using PatchShapes = std::vector;
PatchShapes patch_shapes;
+
+ double used_patches_depth_center = (used_patches_depth[0] + used_patches_depth[1]) / 2;
+
+ // sort used_patches for faster search
+ std::sort(used_shape_patches.begin(), used_shape_patches.end());
for (uint32_t patch_index : shapes_patches[shape_index]) {
// check is patch already used
auto it = std::lower_bound(used_shape_patches.begin(), used_shape_patches.end(), patch_index);
if (it != used_shape_patches.end() && *it == patch_index) continue;
- ExPolygon patch_shape = to_expoly(patches[patch_index], projection);
+
+ // Heuristics to predict which patch to be used need average patch depth
+ Vec2d patche_depth_range(std::numeric_limits::max(), std::numeric_limits::min());
+ ExPolygon patch_shape = to_expoly(patches[patch_index], projection, patche_depth_range);
+ double depth_center = (patche_depth_range[0] + patche_depth_range[1]) / 2;
+ double depth_range_center_distance = std::fabs(used_patches_depth_center - depth_center);
+
ExPolygons patch_intersection = intersection_ex(ExPolygons{patch_shape}, rest);
if (patch_intersection.empty()) continue;
- patch_shapes.push_back({patch_index, patch_shape, patch_intersection});
+ patch_shapes.push_back({patch_index, patch_shape, patch_intersection, depth_range_center_distance});
}
- // how to select which patch to use
- //
- // by depth:
- // how to calc wanted depth - idealy by depth of hole outline points
- //
- // how to calc patch depth - by average outline depth
+ // nothing to add
+ if (patch_shapes.empty()) continue;
+ // only one solution to add
+ if (patch_shapes.size() == 1) {
+ used_shape_patches.push_back(patch_shapes.front().patch_index);
+ continue;
+ }
+ // Idea: Get depth range of used patches and add patches in order by distance to used depth center
+ std::sort(patch_shapes.begin(), patch_shapes.end(), [](const PatchShape &a, const PatchShape &b)
+ { return a.depth_range_center_distance < b.depth_range_center_distance; });
+
+#ifdef DEBUG_OUTPUT_DIR
+ for (const auto &p : patch_shapes) {
+ int gray_level = 30 + (&p - &patch_shapes.front()) * 200 / patch_shapes.size() ;
+ std::stringstream color;
+ color << "#" << std::hex << std::setfill('0') << std::setw(2) << gray_level << gray_level << gray_level;
+ svg.draw(p.shape, color.str());
+ svg.draw(p.intersection, color.str());
+ }
+#endif // DEBUG_OUTPUT_DIR
+
+ for (const PatchShape &patch : patch_shapes) {
+ // Check when exist some place to fill
+ ExPolygons patch_intersection = intersection_ex(patch.intersection, rest);
+ if (patch_intersection.empty()) continue;
+
+ // Extend for sure
+ ExPolygons intersection = offset_ex(patch.intersection, extend_delta);
+ rest = diff_ex(rest, intersection, ApplySafetyOffset::Yes);
+
+ used_shape_patches.push_back(patch.patch_index);
+ if (rest.empty()) break;
+ }
+
+ // QUESTION: How to select which patch to use? How to sort them?
+ // Now is used back projection distance from used patches
+ //
+ // Idealy by outline depth: (need ray cast into patches)
+ // how to calc wanted depth - idealy by depth of outline help to overlap
+ // how to calc patch depth - depth in place of outline position
+ // Which outline to use between
}
-
- // For sure of the bounding boxes intersection
- const double bb_extension = 1e-10;
- const Vec3d bb_ext(bb_extension, bb_extension, bb_extension);
- auto extend_bb = [&bb_ext](const BoundingBoxf3 &bb) {
- return BoundingBoxf3(
- bb.min - bb_ext,
- bb.max + bb_ext);
- };
-
- // queue to flood fill by patches
- std::vector patch_indices;
-
std::vector result(patches.size(), {false});
- for (const ProjectionDistance &d : best_distances) {
- // exist valid projection for shape point?
- if (d.patch_index == std::numeric_limits::max()) continue;
- if (result[d.patch_index]) continue;
- // Add all connected patches
- // This is way to add patche(from other models) without source shape point
- // 1. Patches inside of shape
- // 2. Patches crossing outline between shape points
-
- assert(patch_indices.empty());
- patch_indices.push_back(d.patch_index);
- do {
- size_t patch_index = patch_indices.back();
- patch_indices.pop_back();
- if (result[patch_index]) continue;
+ for (const std::vector &patches: used_shapes_patches)
+ for (uint32_t patch_index : patches) {
+ assert(patch_index < result.size());
+ // check only onece insertation of patch
+ assert(!result[patch_index]);
result[patch_index] = true;
- const SurfacePatch &patch = patches[patch_index];
- BoundingBoxf3 bb = extend_bb(patch.bb);
- for (const SurfacePatch &patch2 : patches) {
- // IMPROVE: check patches only from same shape (ExPolygon)
- size_t patch_index2 = &patch2 - &patches.front();
- // is already filled?
- if (result[patch_index2]) continue;
- // only patches made by same shape could be connected
- if (patch.shape_id != patch2.shape_id) continue;
- BoundingBoxf3 bb2 = extend_bb(patch2.bb);
- if (!bb.intersects(bb2)) continue;
- if (!in_distances[patch_index2]) {
- // TODO: check that really exist shared outline between patches
-
- }
- patch_indices.push_back(patch_index2);
- }
- } while (!patch_indices.empty());
- }
+ }
return result;
}
@@ -3364,7 +3385,7 @@ priv::Loops priv::create_loops(const std::vector &outlines, const CutMesh& m
return loops;
}
-Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &projection)
+Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range)
{
assert(!patch.loops.empty());
if (patch.loops.empty()) return {};
@@ -3374,6 +3395,11 @@ Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &project
polys.reserve(patch.loops.size());
// project conture into 2d space to fillconvert outlines to
+ size_t count = 0;
+ for (const Loop &l : patch.loops) count += l.size();
+ std::vector depths;
+ depths.reserve(count);
+
Points pts;
for (const Loop &l : patch.loops) {
pts.clear();
@@ -3381,14 +3407,17 @@ Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &project
for (VI vi : l) {
const P3 &p3 = patch.mesh.point(vi);
Vec3d p(p3.x(), p3.y(), p3.z());
- std::optional p2_opt = projection.unproject(p);
-
+ double depth;
+ std::optional p2_opt = projection.unproject(p, &depth);
+ if (depth_range[0] > depth) depth_range[0] = depth; // min
+ if (depth_range[1] < depth) depth_range[1] = depth; // max
// Check when appear that skip is enough for poit which can't be unprojected
// - it could break contour
assert(p2_opt.has_value());
if (!p2_opt.has_value()) continue;
- pts.push_back(*p2_opt);
+ pts.push_back(p2_opt->cast());
+ depths.push_back(static_cast(depth));
}
// minimal is triangle
assert(pts.size() >= 3);
@@ -3401,9 +3430,9 @@ Polygons priv::unproject_loops(const SurfacePatch &patch, const Project &project
return polys;
}
-ExPolygon priv::to_expoly(const SurfacePatch &patch, const Project &projection)
+ExPolygon priv::to_expoly(const SurfacePatch &patch, const Project &projection, Vec2d &depth_range)
{
- Polygons polys = unproject_loops(patch, projection);
+ Polygons polys = unproject_loops(patch, projection, depth_range);
// should not be used when no opposit triangle are counted so should not create overlaps
ClipperLib::PolyFillType fill_type = ClipperLib::PolyFillType::pftEvenOdd;
ExPolygons expolys = Slic3r::union_ex(polys, fill_type);
diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp
index e0329a5a0..9fb9e39f7 100644
--- a/src/libslic3r/Emboss.cpp
+++ b/src/libslic3r/Emboss.cpp
@@ -1207,8 +1207,9 @@ Vec3d Emboss::ProjectZ::project(const Vec3d &point) const
return res;
}
-std::optional Emboss::ProjectZ::unproject(const Vec3d &p) const {
- return Point(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE);
+std::optional Emboss::ProjectZ::unproject(const Vec3d &p, double *depth) const {
+ if (depth != nullptr) *depth /= SHAPE_SCALE;
+ return Vec2d(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE);
}
Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
@@ -1284,7 +1285,9 @@ Vec3d Emboss::OrthoProject::project(const Vec3d &point) const
return point + m_direction;
}
-std::optional Emboss::OrthoProject::unproject(const Vec3d &p) const {
+std::optional Emboss::OrthoProject::unproject(const Vec3d &p, double *depth) const
+{
Vec3d pp = m_matrix_inv * p;
- return Point(pp.x(), pp.y());
+ if (depth != nullptr) *depth = pp.z();
+ return Vec2d(pp.x(), pp.y());
}
\ No newline at end of file
diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp
index 83a45a6e6..abe0c125f 100644
--- a/src/libslic3r/Emboss.hpp
+++ b/src/libslic3r/Emboss.hpp
@@ -259,7 +259,13 @@ public:
///
virtual std::pair create_front_back(const Point &p) const = 0;
- virtual std::optional unproject(const Vec3d &p) const = 0;
+ ///
+ /// Back projection
+ ///
+ /// Point to project
+ /// [optional] Depth of 2d projected point. Be careful number is in 2d scale
+ /// Uprojected point when it is possible
+ virtual std::optional unproject(const Vec3d &p, double * depth = nullptr) const = 0;
};
///
@@ -287,7 +293,7 @@ public:
// Inherited via IProject
std::pair create_front_back(const Point &p) const override;
Vec3d project(const Vec3d &point) const override;
- std::optional unproject(const Vec3d &p) const override;
+ std::optional unproject(const Vec3d &p, double * depth = nullptr) const override;
double m_depth;
};
@@ -309,8 +315,10 @@ public:
Vec3d project(const Vec3d &point) const override{
return core->project(point);
}
- std::optional unproject(const Vec3d &p) const override {
- return core->unproject(p / m_scale);
+ std::optional unproject(const Vec3d &p, double *depth = nullptr) const override {
+ auto res = core->unproject(p / m_scale, depth);
+ if (depth != nullptr) *depth *= m_scale;
+ return res;
}
};
@@ -335,7 +343,7 @@ public:
// Inherited via IProject
std::pair create_front_back(const Point &p) const override;
Vec3d project(const Vec3d &point) const override;
- std::optional unproject(const Vec3d &p) const override;
+ std::optional unproject(const Vec3d &p, double * depth = nullptr) const override;
};
};
diff --git a/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
index 09f055040..a16a0c5d4 100644
--- a/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
+++ b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp
@@ -71,6 +71,7 @@ void CreateFontImageJob::process(Ctl &ctl)
m_tex_size = Point(std::ceil(size_f.x()), std::ceil(size_f.y()));
// crop image width
if (m_tex_size.x() > m_input.size.x()) m_tex_size.x() = m_input.size.x();
+ if (m_tex_size.y() > m_input.size.y()) m_tex_size.y() = m_input.size.y();
// Set up result
m_result = std::vector(m_tex_size.x() * m_tex_size.y() * 4, {255});