Add unprojecting of SurfacePatch contours

This commit is contained in:
Filip Sykala - NTB T15p 2022-08-31 08:35:43 +02:00
parent 1f03663b3a
commit 27e640180f
3 changed files with 366 additions and 59 deletions

View File

@ -288,17 +288,31 @@ CutAOIs cut_from_model(CutMesh &cgal_model,
float projection_ratio, float projection_ratio,
const ShapePoint2index &s2i); const ShapePoint2index &s2i);
using Loop = std::vector<VI>;
using Loops = std::vector<Loop>;
/// <summary>
/// Create closed loops of contour vertices created from open half edges
/// </summary>
/// <param name="outlines">Unsorted half edges</param>
/// <param name="mesh">Source mesh for half edges</param>
/// <returns>Closed loops</returns>
Loops create_loops(const std::vector<HI> &outlines, const CutMesh &mesh);
// To track during diff_models, // To track during diff_models,
// what was cutted off, from CutAOI // what was cutted off, from CutAOI
struct SurfacePatch struct SurfacePatch
{ {
// converted cut to CGAL mesh // converted cut to CGAL mesh
// Mesh is reduced.
// (do not contain divided triangles on contour - created by side Quad)
CutMesh mesh; CutMesh mesh;
// CvtVI2VI cvt = mesh.property_map<VI, VI>(patch_source_name); // CvtVI2VI cvt = mesh.property_map<VI, VI>(patch_source_name);
// Conversion VI from this patch to source VI(model) is stored in mesh property // Conversion VI from this patch to source VI(model) is stored in mesh property
// converted source.second to mesh half edges // Outlines - converted CutAOI.second (half edges)
std::vector<HI> outline; // to loops (vertex indicies) by function create_loops
Loops loops;
// bounding box of mesh // bounding box of mesh
BoundingBoxf3 bb; BoundingBoxf3 bb;
@ -311,11 +325,8 @@ struct SurfacePatch
// index of shape from ExPolygons // index of shape from ExPolygons
size_t shape_id = 0; size_t shape_id = 0;
//// Used only during clipping phase // flag that this patch contain whole CutAOI
// flag that part will be deleted bool is_whole_aoi = true;
bool full_inside = false;
// flag that Patch could contain more than one part
bool just_cliped = false;
}; };
using SurfacePatches = std::vector<SurfacePatch>; using SurfacePatches = std::vector<SurfacePatch>;
@ -362,6 +373,30 @@ SurfacePatches diff_models(VCutAOIs &cuts,
/*const*/ CutMeshes &models, /*const*/ CutMeshes &models,
const Project3f &projection); const Project3f &projection);
/// <summary>
/// Checking whether patch is uninterrupted cover of whole expolygon it belongs.
/// </summary>
/// <param name="cutAOI">Part of surface to check</param>
/// <param name="shape">Source shape</param>
/// <param name="mesh">Source of cut</param>
/// <returns>True when cover whole expolygon otherwise false</returns>
bool is_over_whole_expoly(const CutAOI &cutAOI,
const ExPolygon &shape,
const CutMesh &mesh);
/// <summary>
/// Checking whether patch is uninterrupted cover of whole expolygon it belongs.
/// </summary>
/// <param name="patch">Part of surface to check</param>
/// <param name="shape">Source shape</param>
/// <returns>True when cover whole expolygon otherwise false</returns>
bool is_over_whole_expoly(const SurfacePatch &patch,
const ExPolygons &shapes,
const VCutAOIs &cutAOIs,
const CutMeshes &meshes);
ExPolygon to_expoly(const SurfacePatch &patch, const Project &unproject);
/// <summary> /// <summary>
/// To select surface near projection distance /// To select surface near projection distance
/// </summary> /// </summary>
@ -376,9 +411,6 @@ struct ProjectionDistance
// index of Patch // index of Patch
uint32_t patch_index = std::numeric_limits<uint32_t>::max(); uint32_t patch_index = std::numeric_limits<uint32_t>::max();
// index of half edge in AOI
uint32_t hi_index = std::numeric_limits<uint32_t>::max();
// signed distance to projection // signed distance to projection
float distance = std::numeric_limits<float>::max(); float distance = std::numeric_limits<float>::max();
}; };
@ -405,6 +437,21 @@ VDistances calc_distances(const SurfacePatches &patches,
size_t count_shapes_points, size_t count_shapes_points,
float projection_ratio); float projection_ratio);
/// <summary>
/// Fill area of expolygons by patches
/// Not used patches are discarded(removed from vector)
/// </summary>
/// <param name="patches">In/Out parts of surface to be filtered</param>
/// <param name="distances">Depth distances for outline points</param>
/// <param name="shapes">Shapes to be filled by patches</param>
/// <param name="center">Starting point for select correct depth</param>
/// <param name="s2i">Help struct to address point inside of shapes by one index</param>
void filter_patches(SurfacePatches &patches,
const VDistances &distances,
const ExPolygons &shapes,
const Point &center,
const ShapePoint2index &s2i);
/// <summary> /// <summary>
/// Select distances in similar depth between expolygons /// Select distances in similar depth between expolygons
/// </summary> /// </summary>
@ -424,11 +471,9 @@ ProjectionDistances choose_best_distance(
/// </summary> /// </summary>
/// <param name="best_distances"></param> /// <param name="best_distances"></param>
/// <param name="patches"></param> /// <param name="patches"></param>
/// <param name="cuts"></param>
/// <returns>Mask of used patch</returns> /// <returns>Mask of used patch</returns>
std::vector<bool> select_patches(const ProjectionDistances &best_distances, std::vector<bool> select_patches(const ProjectionDistances &best_distances,
const SurfacePatches &patches, const SurfacePatches &patches);
const VCutAOIs &cuts);
/// <summary> /// <summary>
/// Merge masked patches to one surface cut /// Merge masked patches to one surface cut
@ -463,6 +508,8 @@ void store(const Emboss::IProjection &projection, const Point &point_to_project,
#endif // DEBUG_OUTPUT_DIR #endif // DEBUG_OUTPUT_DIR
} // namespace privat } // namespace privat
#include "libslic3r/SVG.hpp"
SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes, SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes,
const std::vector<indexed_triangle_set> &models, const std::vector<indexed_triangle_set> &models,
const Emboss::IProjection &projection, const Emboss::IProjection &projection,
@ -535,15 +582,37 @@ SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes,
for (priv::SurfacePatch &patch : patches) for (priv::SurfacePatch &patch : patches)
patch.shape_id = s2i.calc_id(patch.shape_id).expolygons_index; patch.shape_id = s2i.calc_id(patch.shape_id).expolygons_index;
//// Only for create function
//std::vector<bool> is_filling;
//is_filling.reserve(patches.size());
//for (priv::SurfacePatch &patch : patches)
// is_filling.push_back(priv::is_over_whole_expoly(patch, shapes, model_cuts, cgal_models));
//// convert patch to 2d
//ExPolygons fill;
//fill.reserve(patches.size());
//for (priv::SurfacePatch &patch : patches)
// fill.push_back(to_expoly(patch, projection));
//{
// SVG svg("C:/data/temp/filled_text.svg");
// svg.draw(shapes, "gray");
// svg.draw(fill, "green");
//}
// calc distance to projection for all outline points of cutAOI(shape) // calc distance to projection for all outline points of cutAOI(shape)
// it is used for distiguish the top one // it is used for distiguish the top one
uint32_t shapes_points = s2i.get_count(); uint32_t shapes_points = s2i.get_count();
// for each point collect all projection distances // for each point collect all projection distances
priv::VDistances distances = priv::calc_distances(patches, cgal_models, cgal_shape, shapes_points, projection_ratio); priv::VDistances distances = priv::calc_distances(patches, cgal_models, cgal_shape, shapes_points, projection_ratio);
//assert(shapes_bb.center().x() == 0 && shapes_bb.center().y() == 0);
//Point start = shapes_bb.center();
//priv::filter_patches(patches, distances, shapes, start, s2i);
// for each point select best projection // for each point select best projection
priv::ProjectionDistances best_projection = priv::choose_best_distance(distances, shapes, shapes_bb.center(), s2i); priv::ProjectionDistances best_projection = priv::choose_best_distance(distances, shapes, shapes_bb.center(), s2i);
std::vector<bool> use_patch = priv::select_patches(best_projection, patches, model_cuts); std::vector<bool> use_patch = priv::select_patches(best_projection, patches);
SurfaceCut result = merge_patches(patches, use_patch); SurfaceCut result = merge_patches(patches, use_patch);
#ifdef DEBUG_OUTPUT_DIR #ifdef DEBUG_OUTPUT_DIR
priv::store(result, DEBUG_OUTPUT_DIR + "result.obj", DEBUG_OUTPUT_DIR + "result_contours/"); priv::store(result, DEBUG_OUTPUT_DIR + "result.obj", DEBUG_OUTPUT_DIR + "result_contours/");
@ -1667,9 +1736,9 @@ priv::VDistances priv::calc_distances(const SurfacePatches &patches,
uint32_t patch_index = &patch - &patches.front(); uint32_t patch_index = &patch - &patches.front();
// map is created during patch creation / dividing // map is created during patch creation / dividing
const CvtVI2VI& cvt = patch.mesh.property_map<VI, VI>(patch_source_name).first; const CvtVI2VI& cvt = patch.mesh.property_map<VI, VI>(patch_source_name).first;
// for each half edge of outline // for each point on outline
for (const HI& hi : patch.outline) { for (const Loop &loop : patch.loops)
VI vi_patch = patch.mesh.source(hi); for (const VI &vi_patch : loop) {
VI vi_model = cvt[vi_patch]; VI vi_model = cvt[vi_patch];
if (!vi_model.is_valid()) continue; if (!vi_model.is_valid()) continue;
const IntersectingElement *ie = vert_shape_map[vi_model]; const IntersectingElement *ie = vert_shape_map[vi_model];
@ -1681,10 +1750,10 @@ priv::VDistances priv::calc_distances(const SurfacePatches &patches,
std::vector<ProjectionDistance> &pds = result[pi]; std::vector<ProjectionDistance> &pds = result[pi];
uint32_t model_index = patch.model_id; uint32_t model_index = patch.model_id;
uint32_t aoi_index = patch.aoi_id; uint32_t aoi_index = patch.aoi_id;
uint32_t hi_index = &hi - &patch.outline.front(); //uint32_t hi_index = &hi - &patch.outline.front();
const P3 &p = patch.mesh.point(vi_patch); const P3 &p = patch.mesh.point(vi_patch);
float distance = calc_distance(p, pi, shapes_mesh, projection_ratio); float distance = calc_distance(p, pi, shapes_mesh, projection_ratio);
pds.push_back({model_index, aoi_index, patch_index, hi_index, distance}); pds.push_back({model_index, aoi_index, patch_index, distance});
} }
} }
return result; return result;
@ -2115,7 +2184,72 @@ priv::ClosePoint priv::find_close_point(const Point &p,
return cp; return cp;
} }
// IMPROVE: create better structure to find closest points e.g. Tree void priv::filter_patches(SurfacePatches &patches,
const VDistances &distances,
const ExPolygons &shapes,
const Point &center,
const ShapePoint2index &s2i)
{
assert(distances.size() == count_points(shapes));
// collect closest projection for source shape point(outline point)
ProjectionDistances closest(distances.size());
// store info about finished shapes
std::vector<bool> finished_shapes(shapes.size(), {false});
// wanted distance from ideal projection
// Distances are relative to projection distance
// so first wanted distance is the closest one (ZERO)
float wanted_distance = 0.f;
// For each shape point flag wether exist some distance.
std::vector<bool> mask_distances(s2i.get_count(), {true});
for (const auto &d : distances)
if (d.empty()) mask_distances[&d - &distances.front()] = false;
// Select point from shapes(text contour) which is closest to center (all in 2d)
uint32_t unfinished_index = find_closest_point_index(center, shapes, s2i, mask_distances);
#ifdef DEBUG_OUTPUT_DIR
std::vector<std::pair<size_t, size_t>> connections;
connections.reserve(shapes.size());
connections.emplace_back(unfinished_index, unfinished_index);
#endif // DEBUG_OUTPUT_DIR
do {
const ProjectionDistance* pd = get_closest_projection(distances[unfinished_index], wanted_distance);
// selection of closest_id should proove that pd has value
// (functions: get_closest_point_index and find_close_point_in_points)
assert(pd != nullptr);
fill_shape_distances(unfinished_index, *pd, closest, finished_shapes, s2i, shapes, distances);
// The most close points between finished and unfinished shapes
auto [finished, unfinished] = find_closest_point_pair(
shapes, finished_shapes, s2i, mask_distances);
// detection of end (best doesn't have value)
if (finished == std::numeric_limits<uint32_t>::max()) break;
assert(unfinished != std::numeric_limits<uint32_t>::max());
const ProjectionDistance &closest_pd = closest[finished];
// check that best_cp is finished and has result
assert(closest_pd.aoi_index != std::numeric_limits<uint32_t>::max());
wanted_distance = closest_pd.distance;
unfinished_index = unfinished;
#ifdef DEBUG_OUTPUT_DIR
connections.emplace_back(finished, unfinished);
#endif // DEBUG_OUTPUT_DIR
} while (true); //(unfinished_index != std::numeric_limits<uint32_t>::max());
#ifdef DEBUG_OUTPUT_DIR
store(shapes, mask_distances, connections, DEBUG_OUTPUT_DIR + "closest_points.svg");
#endif // DEBUG_OUTPUT_DIR
}
// IMPROVE2: when select distance fill in all distances from Patch // IMPROVE2: when select distance fill in all distances from Patch
priv::ProjectionDistances priv::choose_best_distance( priv::ProjectionDistances priv::choose_best_distance(
const VDistances &distances, const VDistances &distances,
@ -2594,6 +2728,18 @@ priv::SurfacePatch priv::create_surface_patch(const std::vector<FI> &fis,
// diff_models help functions // diff_models help functions
namespace priv { namespace priv {
struct SurfacePatchEx
{
SurfacePatch patch;
// flag that part will be deleted
bool full_inside = false;
// flag that Patch could contain more than one part
bool just_cliped = false;
};
using SurfacePatchesEx = std::vector<SurfacePatchEx>;
using BBS = std::vector<BoundingBoxf3>; using BBS = std::vector<BoundingBoxf3>;
/// <summary> /// <summary>
/// Create bounding boxes for AOI /// Create bounding boxes for AOI
@ -2670,7 +2816,7 @@ SurfacePatch separate_patch(const std::vector<FI> &fis,
/// </summary> /// </summary>
/// <param name="i">index into patches</param> /// <param name="i">index into patches</param>
/// <param name="patches">In/Out Patches</param> /// <param name="patches">In/Out Patches</param>
void divide_patch(size_t i, SurfacePatches &patches); void divide_patch(size_t i, SurfacePatchesEx &patches);
/// <summary> /// <summary>
/// Fill outline in patches by open edges /// Fill outline in patches by open edges
@ -2791,11 +2937,13 @@ priv::SurfacePatch priv::separate_patch(const std::vector<FI>& fis,
return patch_new; return patch_new;
} }
void priv::divide_patch(size_t i, SurfacePatches &patches) { void priv::divide_patch(size_t i, SurfacePatchesEx &patches)
SurfacePatch &patch = patches[i]; {
assert(patch.just_cliped); SurfacePatchEx &patch_ex = patches[i];
patch.just_cliped = false; assert(patch_ex.just_cliped);
patch_ex.just_cliped = false;
SurfacePatch& patch = patch_ex.patch;
CutMesh& cm = patch.mesh; CutMesh& cm = patch.mesh;
assert(!cm.faces().empty()); assert(!cm.faces().empty());
std::string patch_number_name = "f:patch_number"; std::string patch_number_name = "f:patch_number";
@ -2806,7 +2954,7 @@ void priv::divide_patch(size_t i, SurfacePatches &patches) {
std::vector<FI> fis; std::vector<FI> fis;
fis.reserve(cm.faces().size()); fis.reserve(cm.faces().size());
SurfacePatches new_patches; SurfacePatchesEx new_patches;
std::vector<FI> queue; std::vector<FI> queue;
// IMPROVE: create groups around triangles and than connect groups // IMPROVE: create groups around triangles and than connect groups
for (FI fi_cm : cm.faces()) { for (FI fi_cm : cm.faces()) {
@ -2816,8 +2964,10 @@ void priv::divide_patch(size_t i, SurfacePatches &patches) {
if (!fis.empty()) { if (!fis.empty()) {
// Be carefull after push to patches, // Be carefull after push to patches,
// all ref on patch contain non valid values // all ref on patch contain non valid values
SurfacePatch patch_n = separate_patch(fis, patches[i], cvt_from); SurfacePatchEx patch_ex_n;
new_patches.emplace_back(patch_n); patch_ex_n.patch = separate_patch(fis, patch, cvt_from);
patch_ex_n.patch.is_whole_aoi = false;
new_patches.push_back(std::move(patch_ex_n));
fis.clear(); fis.clear();
} }
// flood fill from triangle fi_cm to surrounding // flood fill from triangle fi_cm to surrounding
@ -2841,7 +2991,7 @@ void priv::divide_patch(size_t i, SurfacePatches &patches) {
// speed up for only one patch - no dividing (the most common) // speed up for only one patch - no dividing (the most common)
if (new_patches.empty()) { if (new_patches.empty()) {
patch.bb = bounding_box(cm); patch.bb = bounding_box(cm);
return; patch.is_whole_aoi = false;
} else { } else {
patch = separate_patch(fis, patch, cvt_from); patch = separate_patch(fis, patch, cvt_from);
patches.insert(patches.end(), new_patches.begin(), new_patches.end()); patches.insert(patches.end(), new_patches.begin(), new_patches.end());
@ -2849,8 +2999,9 @@ void priv::divide_patch(size_t i, SurfacePatches &patches) {
} }
void priv::collect_open_edges(SurfacePatches &patches) { void priv::collect_open_edges(SurfacePatches &patches) {
std::vector<HI> open_half_edges;
for (SurfacePatch &patch : patches) { for (SurfacePatch &patch : patches) {
patch.outline.clear(); open_half_edges.clear();
const CutMesh &mesh = patch.mesh; const CutMesh &mesh = patch.mesh;
for (FI fi : mesh.faces()) { for (FI fi : mesh.faces()) {
HI hi1 = mesh.halfedge(fi); HI hi1 = mesh.halfedge(fi);
@ -2864,9 +3015,11 @@ void priv::collect_open_edges(SurfacePatches &patches) {
for (HI hi : {hi1, hi2, hi3}) { for (HI hi : {hi1, hi2, hi3}) {
HI hi_op = mesh.opposite(hi); HI hi_op = mesh.opposite(hi);
FI fi_op = mesh.face(hi_op); FI fi_op = mesh.face(hi_op);
if (!fi_op.is_valid()) patch.outline.push_back(hi); if (!fi_op.is_valid())
open_half_edges.push_back(hi);
} }
} }
patch.loops = create_loops(open_half_edges, mesh);
} }
} }
@ -2886,8 +3039,11 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
Trees trees(models.size()); Trees trees(models.size());
SurfacePatches patches; SurfacePatches patches;
// queue of patches for one AOI (permanent with respect to for loop) // queue of patches for one AOI (permanent with respect to for loop)
SurfacePatches aoi_patches; SurfacePatchesEx aoi_patches;
//SurfacePatches aoi_patches;
patches.reserve(m2i.get_count()); // only approximation of count patches.reserve(m2i.get_count()); // only approximation of count
size_t index = 0; size_t index = 0;
for (size_t model_index = 0; model_index < models.size(); ++model_index) { for (size_t model_index = 0; model_index < models.size(); ++model_index) {
@ -2899,21 +3055,25 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
for (size_t cut_index = 0; cut_index < model_cuts.size(); ++cut_index, ++index) { for (size_t cut_index = 0; cut_index < model_cuts.size(); ++cut_index, ++index) {
const CutAOI &cut = model_cuts[cut_index]; const CutAOI &cut = model_cuts[cut_index];
SurfacePatch patch = create_surface_patch(cut.first, cut_model_, &vertex_reduction_map); SurfacePatchEx patch_ex;
SurfacePatch &patch = patch_ex.patch;
patch = create_surface_patch(cut.first, cut_model_, &vertex_reduction_map);
patch.bb = bbs[index]; patch.bb = bbs[index];
patch.aoi_id = cut_index; patch.aoi_id = cut_index;
patch.model_id = model_index; patch.model_id = model_index;
patch.shape_id = get_shape_point_index(cut, cut_model); patch.shape_id = get_shape_point_index(cut, cut_model);
patch.is_whole_aoi = true;
aoi_patches.clear(); aoi_patches.clear();
aoi_patches.push_back(patch); aoi_patches.push_back(patch_ex);
for (size_t model_index2 = 0; model_index2 < models.size(); ++model_index2) { for (size_t model_index2 = 0; model_index2 < models.size(); ++model_index2) {
// do not clip source model itself // do not clip source model itself
if (model_index == model_index2) continue; if (model_index == model_index2) continue;
for (SurfacePatch &patch : aoi_patches) { for (SurfacePatchEx &patch_ex : aoi_patches) {
SurfacePatch &patch = patch_ex.patch;
if (has_bb_intersection(patch.bb, model_index2, bbs, m2i) && if (has_bb_intersection(patch.bb, model_index2, bbs, m2i) &&
clip_cut(patch, models[model_index2])){ clip_cut(patch, models[model_index2])){
patch.just_cliped = true; patch_ex.just_cliped = true;
} else { } else {
// build tree on demand // build tree on demand
// NOTE: it is possible not neccessary: e.g. one model // NOTE: it is possible not neccessary: e.g. one model
@ -2925,7 +3085,7 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
tree.build(); tree.build();
} }
if (is_patch_inside_of_model(patch, tree, projection)) if (is_patch_inside_of_model(patch, tree, projection))
patch.full_inside = true; patch_ex.full_inside = true;
} }
} }
// erase full inside // erase full inside
@ -2943,10 +3103,13 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
if (aoi_patches[i].just_cliped) if (aoi_patches[i].just_cliped)
divide_patch(i, aoi_patches); divide_patch(i, aoi_patches);
} }
if (!aoi_patches.empty())
patches.insert(patches.end(), if (!aoi_patches.empty()) {
aoi_patches.begin(), patches.reserve(patches.size() + aoi_patches.size());
aoi_patches.end()); for (SurfacePatchEx &patch : aoi_patches)
patches.push_back(std::move(patch.patch));
}
} }
cut_model_.remove_property_map(vertex_reduction_map); cut_model_.remove_property_map(vertex_reduction_map);
} }
@ -2957,10 +3120,104 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
return patches; return patches;
} }
bool priv::is_over_whole_expoly(const SurfacePatch &patch,
const ExPolygons &shapes,
const VCutAOIs &cutAOIs,
const CutMeshes &meshes)
{
if (!patch.is_whole_aoi) return false;
return is_over_whole_expoly(cutAOIs[patch.model_id][patch.aoi_id],
shapes[patch.shape_id],
meshes[patch.model_id]);
}
bool priv::is_over_whole_expoly(const CutAOI &cutAOI,
const ExPolygon &shape,
const CutMesh &mesh)
{
// NonInterupted contour is without other point and contain all from shape
const VertexShapeMap &vert_shape_map = mesh.property_map<VI, const IntersectingElement*>(vert_shape_map_name).first;
for (HI hi : cutAOI.second) {
const IntersectingElement *ie_s = vert_shape_map[mesh.source(hi)];
const IntersectingElement *ie_t = vert_shape_map[mesh.target(hi)];
if (ie_s == nullptr || ie_t == nullptr)
return false;
assert(ie_s->attr != (unsigned char) IntersectingElement::Type::undefined);
assert(ie_t->attr != (unsigned char) IntersectingElement::Type::undefined);
// check if it is neighbor indices
uint32_t i_s = ie_s->shape_point_index;
uint32_t i_t = ie_t->shape_point_index;
assert(i_s != std::numeric_limits<uint32_t>::max());
assert(i_t != std::numeric_limits<uint32_t>::max());
if (i_s == std::numeric_limits<uint32_t>::max() ||
i_t == std::numeric_limits<uint32_t>::max())
return false;
// made by same index
if (i_s == i_t) continue;
// order from source to target
if (i_s > i_t) {
std::swap(i_s, i_t);
std::swap(ie_s, ie_t);
}
// Must be after fix order !!
bool is_last_polygon_segment = ie_s->is_first() && ie_t->is_last();
if (is_last_polygon_segment) {
std::swap(i_s, i_t);
std::swap(ie_s, ie_t);
}
// Is continous indices
if (!is_last_polygon_segment &&
(ie_s->is_last() || (i_s + 1) != i_t))
return false;
IntersectingElement::Type t_s = ie_s->get_type();
IntersectingElement::Type t_t = ie_t->get_type();
if (t_s == IntersectingElement::Type::undefined ||
t_t == IntersectingElement::Type::undefined)
return false;
// next segment must start with edge intersection
if (t_t != IntersectingElement::Type::edge_1)
return false;
// After face1 must be edge2 or face2
if (t_s == IntersectingElement::Type::face_1)
return false;
}
// When all open edges are on contour than there is NO holes is shape
auto is_open = [&mesh](HI hi)->bool {
HI opposite = mesh.opposite(hi);
return !mesh.face(opposite).is_valid();
};
std::vector<HI> opens; // copy
opens.reserve(cutAOI.second.size());
for (HI hi : cutAOI.second) // from lower to bigger
if (is_open(hi)) opens.push_back(hi);
std::sort(opens.begin(), opens.end());
for (FI fi: cutAOI.first) {
HI face_hi = mesh.halfedge(fi);
for (HI hi : mesh.halfedges_around_face(face_hi)) {
if (!is_open(hi)) continue;
// open edge
auto lb = std::lower_bound(opens.begin(), opens.end(), hi);
if (lb == opens.end() || *lb != hi)
return false; // not in contour
}
}
return true;
}
std::vector<bool> priv::select_patches( std::vector<bool> priv::select_patches(
const ProjectionDistances &best_distances, const ProjectionDistances &best_distances,
const SurfacePatches &patches, const SurfacePatches &patches)
const VCutAOIs &cuts)
{ {
std::vector<bool> in_distances(patches.size(), {false}); std::vector<bool> in_distances(patches.size(), {false});
for (const ProjectionDistance &d : best_distances) { for (const ProjectionDistance &d : best_distances) {
@ -3021,17 +3278,6 @@ std::vector<bool> priv::select_patches(
// help function to 'merge_patches' // help function to 'merge_patches'
namespace priv { namespace priv {
using Loop = std::vector<VI>;
using Loops = std::vector<Loop>;
/// <summary>
/// Create closed loops of contour vertices created from half edges
/// </summary>
/// <param name="outlines">Unsorted half edges</param>
/// <param name="mesh">Source mesh for half edges</param>
/// <returns>Closed loops</returns>
Loops create_loops(const std::vector<HI> &outlines, const CutMesh& mesh);
/// <summary> /// <summary>
/// Convert patch to indexed_triangle_set /// Convert patch to indexed_triangle_set
/// </summary> /// </summary>
@ -3103,6 +3349,51 @@ priv::Loops priv::create_loops(const std::vector<HI> &outlines, const CutMesh& m
return loops; return loops;
} }
#include "ClipperUtils.hpp"
ExPolygon priv::to_expoly(const SurfacePatch &patch, const Project &project)
{
assert(!patch.loops.empty());
if (patch.loops.empty()) return {};
// NOTE: this method is working only when patch did not contain outward faces
Polygons polys;
polys.reserve(patch.loops.size());
// project conture into 2d space to fillconvert outlines to
Points pts;
for (const Loop &l : patch.loops) {
pts.clear();
pts.reserve(l.size());
for (VI vi : l) {
const P3 &p3 = patch.mesh.point(vi);
Vec3f p(p3.x(), p3.y(), p3.z());
std::optional<Point> p2_opt = project.unproject(p);
// 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);
}
// minimal is triangle
assert(pts.size() >= 3);
if (pts.size() < 3) continue;
polys.emplace_back(pts);
}
assert(!polys.empty());
if (polys.empty()) return {};
ExPolygons expolys = Slic3r::union_ex(polys, ClipperLib::PolyFillType::pftEvenOdd);
assert(expolys.size() == 1);
if (expolys.empty()) return {};
return expolys.front();
}
SurfaceCut priv::patch2cut(SurfacePatch &patch) SurfaceCut priv::patch2cut(SurfacePatch &patch)
{ {
CutMesh &mesh = patch.mesh; CutMesh &mesh = patch.mesh;
@ -3144,9 +3435,8 @@ SurfaceCut priv::patch2cut(SurfacePatch &patch)
sc.indices.push_back(ti); sc.indices.push_back(ti);
} }
Loops loops = create_loops(patch.outline, patch.mesh); sc.contours.reserve(patch.loops.size());
sc.contours.reserve(loops.size()); for (const Loop &loop : patch.loops) {
for (const Loop &loop : loops) {
sc.contours.push_back({}); sc.contours.push_back({});
std::vector<SurfaceCut::Index> &contour = sc.contours.back(); std::vector<SurfaceCut::Index> &contour = sc.contours.back();
contour.reserve(loop.size()); contour.reserve(loop.size());

View File

@ -969,6 +969,10 @@ Vec3f Emboss::ProjectZ::project(const Vec3f &point) const
return res; return res;
} }
std::optional<Point> Emboss::ProjectZ::unproject(const Vec3f &p) const {
return Point(p.x() / SHAPE_SCALE, p.y() / SHAPE_SCALE);
}
Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position, Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
const Vec3f &normal, const Vec3f &normal,
float up_limit) float up_limit)
@ -1041,3 +1045,8 @@ Vec3f Emboss::OrthoProject::project(const Vec3f &point) const
{ {
return point + m_direction; return point + m_direction;
} }
std::optional<Point> Emboss::OrthoProject::unproject(const Vec3f &p) const {
Vec3d pp = m_matrix_inv * p.cast<double>();
return Point(pp.x(), pp.y());
}

View File

@ -232,6 +232,8 @@ public:
/// second - back spatial point /// second - back spatial point
/// </returns> /// </returns>
virtual std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const = 0; virtual std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const = 0;
virtual std::optional<Point> unproject(const Vec3f &p) const = 0;
}; };
/// <summary> /// <summary>
@ -259,6 +261,7 @@ public:
// Inherited via IProject // Inherited via IProject
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override; std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override;
Vec3f project(const Vec3f &point) const override; Vec3f project(const Vec3f &point) const override;
std::optional<Point> unproject(const Vec3f &p) const override;
float m_depth; float m_depth;
}; };
@ -280,6 +283,9 @@ public:
Vec3f project(const Vec3f &point) const override{ Vec3f project(const Vec3f &point) const override{
return core->project(point); return core->project(point);
} }
std::optional<Point> unproject(const Vec3f &p) const override {
return core->unproject(p / m_scale);
}
}; };
class OrthoProject3f : public Emboss::IProject3f class OrthoProject3f : public Emboss::IProject3f
@ -295,13 +301,15 @@ public:
Transform3d m_matrix; Transform3d m_matrix;
// size and direction of emboss for ortho projection // size and direction of emboss for ortho projection
Vec3f m_direction; Vec3f m_direction;
Transform3d m_matrix_inv;
public: public:
OrthoProject(Transform3d matrix, Vec3f direction) OrthoProject(Transform3d matrix, Vec3f direction)
: m_matrix(matrix), m_direction(direction) : m_matrix(matrix), m_direction(direction), m_matrix_inv(matrix.inverse())
{} {}
// Inherited via IProject // Inherited via IProject
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override; std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override;
Vec3f project(const Vec3f &point) const override; Vec3f project(const Vec3f &point) const override;
std::optional<Point> unproject(const Vec3f &p) const override;
}; };
}; };