diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp
index 6cc9dab512..80c538e98e 100644
--- a/src/libslic3r/CutSurface.cpp
+++ b/src/libslic3r/CutSurface.cpp
@@ -398,7 +398,12 @@ bool is_almost_parallel(FI fi,
/// In/Out map with faces type
void flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map);
-using ReductionMap = CutMesh::Property_map;
+// Conversion one vertex index to another
+using CvtVI2VI = CutMesh::Property_map;
+// Each Patch track outline vertex conversion to tource model
+const std::string patch_source_name = "v:patch_source";
+
+using ReductionMap = CvtVI2VI;
///
/// Create map to reduce unnecesary triangles,
/// Triangles are made by divided quad to two triangles
@@ -474,6 +479,53 @@ CutAOIs create_cut_area_of_interests(const CutMesh &mesh,
const ExPolygons &shapes,
FaceTypeMap &face_type_map);
+// To track during diff_models,
+// what was cutted off, from CutAOI
+struct SurfacePatch
+{
+ // converted cut to CGAL mesh
+ CutMesh mesh;
+ // CvtVI2VI cvt = mesh.property_map(patch_source_name);
+ // Conversion VI from this patch to source VI(model) is stored in mesh property
+
+ // converted source.second to mesh half edges
+ std::vector outline;
+
+ BoundingBoxf3 bb;
+
+ //// Data needed to find best projection distances
+ // index of source model in models
+ size_t model_id;
+ // index of source CutAOI
+ size_t aoi_id;
+
+ // index of shape from ExPolygons
+ size_t shape_id = 0;
+ // TODO: fill by point from AOI outline
+
+ //// Used only during clipping phase
+ // flag that part will be deleted
+ bool full_inside = false;
+ // flag that Patch could contain more than one part
+ bool just_cliped = false;
+};
+using SurfacePatches = std::vector;
+
+///
+/// Differenciate other models
+///
+/// Patches from meshes
+/// Convert model_index and cut_index into one index
+/// Source points for Cutted AOIs
+/// Original models without cut modifications used for
+/// differenciate Define projection
+/// direction Cuts differenciate by models - Patch
+SurfacePatches diff_models(VCutAOIs &cuts,
+ const ModelCut2index &m2i,
+ const CutMeshes &cut_models,
+ /*const*/ CutMeshes &models,
+ const Emboss::IProject3f &projection);
+
///
/// To select correct area
///
@@ -485,6 +537,9 @@ struct ProjectionDistance
// index of CutAOI
uint32_t aoi_index = std::numeric_limits::max();
+ // index of Patch
+ uint32_t patch_index = std::numeric_limits::max();
+
// index of half edge in AOI
uint32_t hi_index = std::numeric_limits::max();
@@ -498,23 +553,23 @@ using ProjectionDistances = std::vector;
using VDistances = std::vector;
///
-/// Calculate distances from CutAOI contour points to ProjectionOrigin
+/// Calculate distances for SurfacePatches outline points
/// NOTE:
/// each model has to have "vert_shape_map" .. Know source of new vertices
///
-/// models AOIs
-/// Count of contour points in shapes
+/// Part of surface
/// Vertices position
/// Mesh created by shapes
+/// Count of contour points in shapes
/// Define best distnace
-/// Know source of new vertices
/// Projection distances of cutted shape points
-VDistances calc_distances(const VCutAOIs &cuts,
- size_t count_shapes_points,
+VDistances calc_distances(const SurfacePatches &patches,
const CutMeshes &models,
const CutMesh &shapes_mesh,
+ size_t count_shapes_points,
float projection_ratio);
- ///
+
+///
/// Select distances in similar depth between expolygons
///
/// All distances
@@ -526,6 +581,27 @@ ProjectionDistances choose_best_distance(
const ExPolygons &shapes,
const ShapePoint2index &shape_point_2_index);
+///
+/// Create mask for patches
+///
+///
+///
+///
+/// Mask of used patch
+std::vector select_patches(const ProjectionDistances &best_distances,
+ const SurfacePatches &patches,
+ const VCutAOIs &cuts);
+
+///
+/// Merge masked patches to one surface cut
+///
+/// All patches
+/// NOTE: need add property for transform
+/// Mash for using patch
+/// Result surface cut
+SurfaceCut merge_patches(/*const*/ SurfacePatches &patches,
+ std::vector mask);
+
using ConvertMap = CutMesh::Property_map;
///
/// Create surface cuts from mesh model
@@ -640,45 +716,6 @@ SurfaceCut::CutContour create_cut(const std::vector &outlines,
/// Merged all surface cuts into one
SurfaceCut merge_intersections(SurfaceCuts &cuts, const CutAOIs& cutAOIs, const std::vector& use_cut);
-// To track what was cutted of
-struct SurfacePatch
-{
- // converted cut to CGAL mesh
- CutMesh mesh;
- // converted source.second to mesh half edges
- std::vector outline;
-
- BoundingBoxf3 bb;
-
- /// used during process
- // flag that part will be deleted
- bool full_inside = false;
- // flag that Patch could contain more than one part
- bool just_cliped = false;
-};
-using SurfacePatches = std::vector;
-
-///
-/// Differenciate other models
-///
-/// Patches from meshes
-/// Convert model_index and cut_index into one index
-/// Source points for Cutted AOIs
-/// Original models without cut modifications used for differenciate
-/// Define projection direction
-/// Cuts differenciate by models - Patch
-SurfacePatches diff_models(VCutAOIs &cuts,
- const ModelCut2index &m2i,
- const CutMeshes &cut_models,
- /*const*/ CutMeshes &models,
- const Emboss::IProject3f &projection);
-
-// keep CGAL Mesh for next processing
-struct SurfaceCutWithMesh : public SurfaceCut{
- CutMesh cgalMesh = CutMesh();
-};
-
-
///
/// Merge 2 Cuts when has intersection
///
@@ -707,7 +744,7 @@ void store(const ProjectionDistances &pds, const VCutAOIs &aois, const CutMeshes
void store(const SurfaceCuts &cut, const std::string &dir);
void store(const std::vector &models, const std::string &obj_filename);
-void store(const std::vector&models, const std::string &off_filename);
+void store(const std::vector&models, const std::string &dir);
void store(const Emboss::IProjection &projection, const Point &point_to_project, float projection_ratio, const std::string &obj_filename);
#endif // DEBUG_OUTPUT_DIR
} // namespace privat
@@ -755,7 +792,8 @@ SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes,
}
#ifdef DEBUG_OUTPUT_DIR
- priv::store(cgal_models, DEBUG_OUTPUT_DIR + "model");// model[0-N].off
+ priv::store(cgal_models, DEBUG_OUTPUT_DIR + "model/");// model[0-N].off
+ priv::store(cgal_neg_models, DEBUG_OUTPUT_DIR + "model_neg/"); // model[0-N].off
#endif // DEBUG_OUTPUT_DIR
priv::CutMesh cgal_shape = priv::to_cgal(shapes, projection);
@@ -778,33 +816,35 @@ SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes,
model_cuts.push_back(std::move(cutAOIs));
}
+ // Differenciate other models from CutAOI (Cutted Area of Interest)
priv::ModelCut2index m2i(model_cuts);
priv::SurfacePatches patches = priv::diff_models(model_cuts, m2i, cgal_models, cgal_neg_models, projection);
#ifdef DEBUG_OUTPUT_DIR
priv::store(patches, DEBUG_OUTPUT_DIR + "patches/");
#endif // DEBUG_OUTPUT_DIR
+ // fix - convert shape_point_id to expolygon index
+ // save 1 param(s2i) from diff_models call
+ for (priv::SurfacePatch &patch : patches)
+ patch.shape_id = s2i.calc_id(patch.shape_id).expolygons_index;
+
// calc distance to projection for all outline points of cutAOI(shape)
// it is used for distiguish the top one
uint32_t shapes_points = s2i.get_count();
// for each point collect all projection distances
- priv::VDistances distances = priv::calc_distances(
- model_cuts, shapes_points, cgal_models, cgal_shape, projection_ratio);
+ priv::VDistances distances = priv::calc_distances(patches, cgal_models, cgal_shape, shapes_points, projection_ratio);
// for each point select best projection
- priv::ProjectionDistances best_projection =
- priv::choose_best_distance(distances, shapes, s2i);
-
+ priv::ProjectionDistances best_projection = priv::choose_best_distance(distances, shapes, s2i);
#ifdef DEBUG_OUTPUT_DIR
- store(best_projection, model_cuts, cgal_models, DEBUG_OUTPUT_DIR + "best_projection.obj"); // only debug
+ // TODO: fix by using of patches
+ //store(best_projection, model_cuts, cgal_models, DEBUG_OUTPUT_DIR + "best_projection.obj"); // only debug
#endif // DEBUG_OUTPUT_DIR
- // Create mask for wanted AOIs
- std::vector is_best_cut(m2i.get_count(), {false});
- for (const priv::ProjectionDistance& d : best_projection)
- if (d.model_index != std::numeric_limits::max())
- is_best_cut[m2i.calc_index({d.model_index, d.aoi_index})] = true;
+ std::vector use_patch = priv::select_patches(best_projection, patches, model_cuts);
+ SurfaceCut result = merge_patches(patches, use_patch);
+ return result;
// IMPROVE: create reduce map on demand - may be model do not need it (when it is not used for result)
// Reduction prepare
@@ -842,8 +882,6 @@ SurfaceCut Slic3r::cut_surface(const ExPolygons &shapes,
// Self Intersection of surface cuts are
// made by damaged models AND multi volumes
//SurfaceCut result = priv::merge_intersections(surface_cuts, cutAOIs, is_best_cut);
-
- SurfaceCut result;
//for (SurfaceCuts &cuts : model_cuts)
// for (SurfaceCut &cut : cuts) append(result, std::move(cut));
//#ifdef DEBUG_OUTPUT_DIR
@@ -2087,60 +2125,86 @@ priv::CutAOIs priv::create_cut_area_of_interests(const CutMesh &mesh,
return result;
}
-priv::VDistances priv::calc_distances(const std::vector &cuts,
- size_t count_shapes_points,
- const std::vector &models,
- const CutMesh &shapes_mesh,
- float projection_ratio)
+namespace priv {
+
+///
+/// Calculate projection distance of point [in mm]
+///
+/// Point to calc distance
+/// Index of point on contour
+/// Model of cutting shape
+/// Ratio for best projection distance
+/// Distance of point from best projection
+float calc_distance(const P3 &p,
+ uint32_t pi,
+ const CutMesh &shapes_mesh,
+ float projection_ratio);
+
+}
+
+float priv::calc_distance(const P3 &p,
+ uint32_t pi,
+ const CutMesh &shapes_mesh,
+ float projection_ratio)
{
- // calculate distance from projection ration [in mm]
- auto calc_distance = [&models, &shapes_mesh, projection_ratio](uint32_t pi, VI vi, uint32_t model_index) -> float {
- const P3& p = models[model_index].point(vi);
-
- // It is known because shapes_mesh is created inside of private space
- VI vi_start(2 * pi);
- VI vi_end(2 * pi + 1);
+ // It is known because shapes_mesh is created inside of private space
+ VI vi_start(2 * pi);
+ VI vi_end(2 * pi + 1);
- // Get range for intersection
- const P3 &start = shapes_mesh.point(vi_start);
- const P3 &end = shapes_mesh.point(vi_end);
+ // Get range for intersection
+ const P3 &start = shapes_mesh.point(vi_start);
+ const P3 &end = shapes_mesh.point(vi_end);
- size_t max_i = 0;
- float max_val = 0.f;
- for (size_t i = 0; i < 3; i++) {
- float val = start[i] - end[i];
- // abs value
- if (val < 0.f) val *= -1.f;
- if (max_val < val) {
- max_val = val;
- max_i = i;
- }
+ // find index in vector with biggest difference
+ size_t max_i = 0;
+ float max_val = 0.f;
+ for (size_t i = 0; i < 3; i++) {
+ float val = start[i] - end[i];
+ // abs value
+ if (val < 0.f) val *= -1.f;
+ if (max_val < val) {
+ max_val = val;
+ max_i = i;
}
- return (p[max_i] - start[max_i]) - projection_ratio * (end[max_i] - start[max_i]);
- };
+ }
+ float from_start = p[max_i] - start[max_i];
+ float best_distance = projection_ratio * (end[max_i] - start[max_i]);
+ return from_start - best_distance;
+}
+
+priv::VDistances priv::calc_distances(const SurfacePatches &patches,
+ const CutMeshes &models,
+ const CutMesh &shapes_mesh,
+ size_t count_shapes_points,
+ float projection_ratio)
+{
priv::VDistances result(count_shapes_points);
- for (uint32_t model_index = 0; model_index < cuts.size(); model_index++) {
- const CutAOIs& model_cuts = cuts[model_index];
+ for (const SurfacePatch &patch : patches) {
// map is created during intersection by corefine visitor
const VertexShapeMap &vert_shape_map =
- models[model_index].property_map(vert_shape_map_name).first;
- for (const CutAOI &cut : model_cuts) {
- // for each half edge of outline
- for (const HI& hi : cut.second) {
- VI vi = models[model_index].source(hi);
- const IntersectingElement * ie = vert_shape_map[vi];
- if (ie == nullptr) continue;
- assert(ie->shape_point_index != std::numeric_limits::max());
- assert(ie->attr != (unsigned char) IntersectingElement::Type::undefined);
- uint32_t pi = ie->shape_point_index;
- assert(pi <= count_shapes_points);
- std::vector &pds = result[pi];
- uint32_t aoi_index = &cut - &model_cuts.front();
- uint32_t hi_index = &hi - &cut.second.front();
- float distance = calc_distance(pi, vi, model_index);
- pds.push_back({model_index, aoi_index, hi_index, distance});
- }
+ models[patch.model_id].property_map(vert_shape_map_name).first;
+ uint32_t patch_index = &patch - &patches.front();
+ // map is created during patch creation / dividing
+ const CvtVI2VI& cvt = patch.mesh.property_map(patch_source_name).first;
+ // for each half edge of outline
+ for (const HI& hi : patch.outline) {
+ VI vi_patch = patch.mesh.source(hi);
+ VI vi_model = cvt[vi_patch];
+ if (!vi_model.is_valid()) continue;
+ const IntersectingElement *ie = vert_shape_map[vi_model];
+ if (ie == nullptr) continue;
+ assert(ie->shape_point_index != std::numeric_limits::max());
+ assert(ie->attr != (unsigned char) IntersectingElement::Type::undefined);
+ uint32_t pi = ie->shape_point_index;
+ assert(pi <= count_shapes_points);
+ std::vector &pds = result[pi];
+ uint32_t model_index = patch.model_id;
+ uint32_t aoi_index = patch.aoi_id;
+ uint32_t hi_index = &hi - &patch.outline.front();
+ const P3 &p = patch.mesh.point(vi_patch);
+ float distance = calc_distance(p, pi, shapes_mesh, projection_ratio);
+ pds.push_back({model_index, aoi_index, patch_index, hi_index, distance});
}
}
return result;
@@ -2167,11 +2231,6 @@ uint32_t get_closest_point_index(const Point &p, const ExPolygons &shapes, const
// Search for closest projection to wanted distance
const ProjectionDistance *get_closest_projection(const ProjectionDistances &distance, float wanted_distance);
-// return neighbor projection distance when exists
-const ProjectionDistance *get_next(const ProjectionDistance &from_pd,
- const ProjectionDistances &from,
- const ProjectionDistances &to);
-
// fill result around known index inside one polygon
void fill_polygon_distances(const ProjectionDistance &pd, uint32_t index, const ShapePointId &id, ProjectionDistances & result, const ExPolygons &shapes, const VDistances &distances);
@@ -2233,55 +2292,6 @@ const priv::ProjectionDistance *priv::get_closest_projection(
return min_pd;
}
-const priv::ProjectionDistance *priv::get_next(
- const ProjectionDistance &from_pd,
- const ProjectionDistances &from,
- const ProjectionDistances &to)
-{
- // exist some projection?
- if (to.empty()) return {};
-
- // find next same aoi (closest one)
- const ProjectionDistance* to_pd = nullptr;
- for (const ProjectionDistance &t : to) {
- if (t.aoi_index != from_pd.aoi_index) continue;
- if (to_pd != nullptr) {
- // when exist more than one use closest to previous
- float distance_prev = std::fabs(to_pd->distance - from_pd.distance);
- float distance = std::fabs(t.distance - from_pd.distance);
- if (distance < distance_prev)
- to_pd = &t;
- } else {
- to_pd = &t;
- }
- }
-
- if (to_pd != nullptr) {
- // detect crossing aois
- const ProjectionDistance* cross_pd = nullptr;
- for (const ProjectionDistance &t : to) {
- if (t.distance > to_pd->distance) continue;
- for (const ProjectionDistance &f : from) {
- if (f.aoi_index != t.aoi_index) continue;
- if (f.distance < from_pd.distance) continue;
- if (cross_pd!=nullptr) {
- // multiple crossing
- if (cross_pd->distance > f.distance)
- cross_pd = &f;
- } else {
- cross_pd = &f;
- }
- }
- }
- // TODO: Detect opposit crossing - should be fixed
- if (cross_pd!=nullptr) return cross_pd;
- } else {
- // Try find another closest AOI
- return get_closest_projection(to, from_pd.distance);
- }
- return to_pd;
-}
-
void priv::fill_polygon_distances(const ProjectionDistance &pd,
uint32_t index,
const ShapePointId &id,
@@ -2299,15 +2309,13 @@ void priv::fill_polygon_distances(const ProjectionDistance &pd,
uint32_t act_index = index;
const ProjectionDistance* act_pd = &pd;
- const ProjectionDistances* act_distances = &distances[act_index];
// Copy starting pd to result
result[act_index] = pd;
- auto exist_next = [&distances, &act_index, &act_pd, &act_distances, &result]
+ auto exist_next = [&distances, &act_index, &act_pd, &result]
(uint32_t nxt_index) {
- const ProjectionDistances* nxt_distances = &distances[nxt_index];
- const ProjectionDistance *nxt_pd = get_next(*act_pd, *act_distances, *nxt_distances);
+ const ProjectionDistance *nxt_pd = get_closest_projection(distances[nxt_index] ,act_pd->distance);
// exist next projection distance ?
if (nxt_pd == nullptr) return false;
@@ -2318,8 +2326,7 @@ void priv::fill_polygon_distances(const ProjectionDistance &pd,
// next
act_index = nxt_index;
- act_pd = &result[nxt_index];
- act_distances = nxt_distances;
+ act_pd = &result[nxt_index];
return true;
};
@@ -2338,9 +2345,8 @@ void priv::fill_polygon_distances(const ProjectionDistance &pd,
// when all results for polygon are set no neccessary to iterate negative
if (act_index == finish_index) return;
- act_index = index;
- act_pd = &pd;
- act_distances = &distances[act_index];
+ act_index = index;
+ act_pd = &pd;
// Negative iteration inside polygon
do {
uint32_t nxt_index = (act_index == first_index) ?
@@ -2677,9 +2683,6 @@ bool priv::merge_intersection(SurfaceCut &cut1, const SurfaceCut &cut2) {
return false;
}
-namespace priv {
-} // namespace priv
-
void priv::create_face_types(FaceTypeMap &map,
const CutMesh &tm1,
const CutMesh &tm2,
@@ -2927,7 +2930,7 @@ priv::SurfacePatch priv::create_surface_patch(CutAOI &cut, const CutMesh &mesh)
}
uint32_t count_faces = cut.first.size();
- // NOTE: It is more than neccessary, guess it better
+ // IMPROVE: Value is greater than neccessary, guess it better
uint32_t count_edges = count_faces*3;
CutMesh cm;
@@ -2964,9 +2967,129 @@ priv::SurfacePatch priv::create_surface_patch(CutAOI &cut, const CutMesh &mesh)
outline.push_back(hi_cvt);
}
+ // convert VI from this patch to source VI, when exist
+ CvtVI2VI& cvt = cm.add_property_map(patch_source_name).first;
+ // vi_s .. VertexIndex into mesh (source)
+ // vi_d .. new VertexIndex in cm (destination)
+ for (uint32_t vi_s = 0; vi_s < v_cvt.size(); ++vi_s) {
+ uint32_t vi_d = v_cvt[vi_s];
+ if (vi_d == def_val) continue;
+ // check only one conversion
+ assert(!cvt[VI(vi_d)].is_valid());
+ cvt[VI(vi_d)] = VI(vi_s);
+ }
+
return {std::move(cm), std::move(outline)};
}
+namespace priv {
+///
+/// Create bounding boxes for AOI
+///
+/// Cutted AOI from models
+/// Source points of cuts
+/// Bounding boxes
+std::vector create_bbs(const VCutAOIs &cuts,
+ const CutMeshes &cut_models);
+
+///
+/// Separate connected triangles into it's own patches
+/// new patches are added to back of input patches
+///
+/// index into patches
+/// In/Out Patches
+void divide_patch(size_t i, SurfacePatches &patches);
+
+}
+
+std::vector priv::create_bbs(const VCutAOIs &cuts,
+ const CutMeshes &cut_models)
+{
+ size_t count = 0;
+ for (const CutAOIs &cut : cuts) count += cut.size();
+
+ std::vector bbs;
+ bbs.reserve(count);
+ for (size_t model_index = 0; model_index < cut_models.size(); ++model_index) {
+ const CutMesh &cut_model = cut_models[model_index];
+ const CutAOIs &cutAOIs = cuts[model_index];
+ for (size_t cut_index = 0; cut_index < cutAOIs.size(); ++cut_index) {
+ const CutAOI &cut = cutAOIs[cut_index];
+ bbs.push_back(bounding_box(cut, cut_model));
+ }
+ }
+ return bbs;
+}
+
+void priv::divide_patch(size_t i, SurfacePatches &patches) {
+ SurfacePatch &patch = patches[i];
+ assert(patch.just_cliped);
+ patch.just_cliped = false;
+
+ constexpr size_t def_value = std::numeric_limits::max();
+
+ CutMesh& cm = patch.mesh;
+ std::string patch_number_name = "f:patch_number";
+ auto patch_number = cm.add_property_map(patch_number_name, {def_value}).first;
+
+ size_t number = 0;
+ std::vector queue;
+ // IMPROVE: create groups around triangles and than connect groups
+ for (FI fi_cm : cm.faces()) {
+ if (patch_number[fi_cm] != def_value) continue;
+ assert(queue.empty());
+ queue.push_back(fi_cm);
+ // flood fill from triangle fi_cm to surrounding
+ do {
+ FI fi_q = queue.back();
+ queue.pop_back();
+ if (patch_number[fi_q] != def_value) {
+ assert(patch_number[fi_q] == number);
+ continue;
+ }
+ patch_number[fi_q] = number;
+ HI hi = cm.halfedge(fi_q);
+ for (FI fi : cm.faces_around_face(hi)) {
+ // by documentation The face descriptor may be the null face, and it may be several times the same face descriptor.
+ if (!fi.is_valid()) continue;
+ if (patch_number[fi] == def_value) queue.push_back(fi);
+ }
+ } while (!queue.empty());
+ ++number;
+ }
+
+ // speed up for only one patch - no dividing (the most common)
+ if (number == 1) {
+ cm.remove_property_map(patch_number);
+ patch.bb = bounding_box(cm);
+ // TODO: fix outline
+ return;
+ }
+
+ const CvtVI2VI& cvt_from = patch.mesh.property_map(patch_source_name).first;
+ auto separate_patch = [&patch_number, &patch, &cvt_from](size_t n) -> SurfacePatch {
+ CutAOI cut;
+ for (FI fi_cm : patch.mesh.faces())
+ if (patch_number[fi_cm] == n) cut.first.push_back(fi_cm);
+ SurfacePatch patch_new = create_surface_patch(cut, patch.mesh);
+ patch_new.bb = bounding_box(patch_new.mesh);
+ patch_new.aoi_id = patch.aoi_id;
+ patch_new.model_id = patch.model_id;
+ patch_new.shape_id = patch.shape_id;
+ // fix cvt
+ CvtVI2VI& cvt = patch_new.mesh.property_map(patch_source_name).first;
+ for (VI &vi : cvt) {
+ if (!vi.is_valid()) continue;
+ vi = cvt_from[vi];
+ }
+ return patch_new;
+ };
+
+ for (size_t n = 1; n < number; n++)
+ patches.push_back(separate_patch(n));
+ patch = separate_patch(0);
+}
+
priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
const ModelCut2index &m2i,
const CutMeshes &cut_models,
@@ -2974,16 +3097,7 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
const Emboss::IProject3f &projection)
{
// create bounding boxes for cuts
- std::vector bbs;
- bbs.reserve(m2i.get_count());
- for (size_t model_index = 0; model_index < models.size(); ++model_index) {
- const CutMesh &cut_model = cut_models[model_index];
- const CutAOIs &cutAOIs = cuts[model_index];
- for (size_t cut_index = 0; cut_index < cutAOIs.size(); ++cut_index) {
- const CutAOI &cut = cutAOIs[cut_index];
- bbs.push_back(bounding_box(cut, cut_model));
- }
- }
+ std::vector bbs = create_bbs(cuts, cut_models);
// check whether cut has intersection with model
auto has_bb_intersection = [&bbs, &m2i, &cuts]
@@ -2996,7 +3110,7 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
return false;
};
- // Do not make Tree twice, when exist out of cut function
+ // IMPROVE: Do not make Tree twice, when exist out of cut function
using Primitive = CGAL::AABB_face_graph_triangle_primitive;
using Traits = CGAL::AABB_traits;
using Ray = EpicKernel::Ray_3;
@@ -3012,7 +3126,8 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
}
// only for model without intersection
- // use ray from any point in projection direction
+ // use ray (in projection direction) from a point from patch
+ // and count intersections: pair .. outside | odd .. inside
auto is_patch_inside_of_model = [&trees, &projection]
(SurfacePatch &patch, size_t model_index) {
// TODO: Solve model with hole in projection direction !!!
@@ -3040,67 +3155,24 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
return is_in;
};
- // separate connected triangles into it's own patches
- // new patches are added to back of input patches
- auto divide_patch = [](size_t i, SurfacePatches &patches) {
- SurfacePatch &patch = patches[i];
- assert(patch.just_cliped);
- patch.just_cliped = false;
-
- constexpr size_t def_value = std::numeric_limits::max();
-
- CutMesh& cm = patch.mesh;
- std::string patch_number_name = "f:patch_number";
- auto patch_number = cm.add_property_map(patch_number_name, {def_value}).first;
-
- size_t number = 0;
- std::vector queue;
- // IMPROVE: create groups around triangles and than connect groups
- for (FI fi_cm : cm.faces()) {
- if (patch_number[fi_cm] != def_value) continue;
- assert(queue.empty());
- queue.push_back(fi_cm);
- // flood fill from triangle fi_cm to surrounding
- do {
- FI fi_q = queue.back();
- queue.pop_back();
- if (patch_number[fi_q] != def_value) {
- assert(patch_number[fi_q] == number);
- continue;
- }
- patch_number[fi_q] = number;
- HI hi = cm.halfedge(fi_q);
- for (FI fi : cm.faces_around_face(hi)) {
- // by documentation The face descriptor may be the null face, and it may be several times the same face descriptor.
- if (!fi.is_valid()) continue;
- if (patch_number[fi] == def_value) queue.push_back(fi);
- }
- } while (!queue.empty());
- ++number;
+ // used to find expolygon index
+ auto get_shape_point_index = []
+ (const CutAOI &cut, const CutMesh &model) -> uint32_t{
+ // map is created during intersection by corefine visitor
+ const VertexShapeMap &vert_shape_map = model.property_map(vert_shape_map_name).first;
+ // for each half edge of outline
+ for (HI hi : cut.second) {
+ VI vi = model.source(hi);
+ const IntersectingElement *ie = vert_shape_map[vi];
+ if (ie == nullptr) continue;
+ assert(ie->shape_point_index != std::numeric_limits::max());
+ return ie->shape_point_index;
}
-
- // speed up for only one patch - no dividing (the most common)
- if (number == 1) {
- cm.remove_property_map(patch_number);
- patch.bb = bounding_box(cm);
- return;
- }
-
- auto separate_patch = [&patch_number, &cm](size_t n) -> SurfacePatch {
- CutAOI cut;
- for (FI fi_cm : cm.faces())
- if (patch_number[fi_cm] == n) cut.first.push_back(fi_cm);
- SurfacePatch patch = create_surface_patch(cut, cm);
- patch.bb = bounding_box(patch.mesh);
- return patch;
- };
-
- for (size_t n = 1; n < number; n++)
- patches.push_back(separate_patch(n));
- patch = separate_patch(0);
+ // can't found any intersecting element in cut
+ assert(false);
+ return 0;
};
- std::vector removed(m2i.get_count(), {false});
SurfacePatches patches;
// queue of patches for one AOI (permanent with respect to for loop)
SurfacePatches aoi_patches;
@@ -3113,6 +3185,10 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
CutAOI &cut = model_cuts[cut_index];
SurfacePatch patch = create_surface_patch(cut, cut_model);
patch.bb = bbs[index];
+ patch.aoi_id = index;
+ patch.model_id = model_index;
+ patch.shape_id = get_shape_point_index(cut, cut_model);
+
aoi_patches.clear();
aoi_patches.push_back(patch);
for (size_t model_index2 = 0; model_index2 < models.size(); ++model_index2) {
@@ -3130,36 +3206,112 @@ priv::SurfacePatches priv::diff_models(VCutAOIs &cuts,
auto it = aoi_patches.begin() + (i - 1);
if (it->full_inside) aoi_patches.erase(it);
}
- if (aoi_patches.empty()) {
- removed[index] = true;
- break;
- }
+
+ // detection of full AOI inside of model
+ if (aoi_patches.empty()) break;
+
// divide cliped into parts
size_t end = aoi_patches.size();
for (size_t i = 0; i < end; ++i)
if (aoi_patches[i].just_cliped)
divide_patch(i, aoi_patches);
}
- if (!removed[index])
+ if (!aoi_patches.empty())
patches.insert(patches.end(),
aoi_patches.begin(),
aoi_patches.end());
}
- }
+ }
+
+ // fill outlines
+ // Also use outline inside of patches(made by non manifold models)
+ // IMPROVE: trace outline from AOIs
+ for (SurfacePatch &patch : patches) {
+ patch.outline.clear();
+ const CutMesh &mesh = patch.mesh;
+ for (FI fi : mesh.faces()) {
+ HI hi1 = mesh.halfedge(fi);
+ assert(hi1.is_valid());
+ HI hi2 = mesh.next(hi1);
+ assert(hi2.is_valid());
+ HI hi3 = mesh.next(hi2);
+ assert(hi3.is_valid());
+ // Is fi triangle?
+ assert(mesh.next(hi3) == hi1);
+ for (HI hi : {hi1, hi2, hi3}) {
+ HI hi_op = mesh.opposite(hi);
+ FI fi_op = mesh.face(hi_op);
+ if (!fi_op.is_valid())
+ patch.outline.push_back(hi);
+ }
+ }
+ }
+
return patches;
// TODO: reduce outlines
// TODO: add cutting edge
-
+ //
// TODO: merge AOIs without intersection - only append
-
- // Cuts merged in are signed in finished vector as TRUE
- // All rest cuts must be merged simple way
- //SurfaceCut result;
- //for (size_t cut_index = 0; cut_index < cuts.size(); ++cut_index) {
- // if (finished[cut_index]) continue;
- // append(result, std::move(cuts[cut_index]));
- //}
- //return result;
+}
+
+
+std::vector priv::select_patches(
+ const ProjectionDistances &best_distances,
+ const SurfacePatches &patches,
+ const VCutAOIs &cuts)
+{
+ std::vector in_distances(patches.size(), {false});
+ for (const ProjectionDistance &d : best_distances)
+ in_distances[d.patch_index] = true;
+
+ // 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) {
+ if (result[d.patch_index]) continue;
+ // Add all connected patches
+ // This is way to add patche 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;
+ 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;
}
bool Slic3r::merge_intersection(SurfaceCut& sc1, const SurfaceCut& sc2) {
@@ -3240,6 +3392,75 @@ SurfaceCut priv::merge_intersections(
return result;
}
+// help function to 'merge_patches'
+namespace priv {
+///
+/// Convert patch to indexed_triangle_set
+///
+/// Part of surface
+/// Converted patch
+SurfaceCut patch2cut(SurfacePatch &patch);
+} // namespace priv
+
+SurfaceCut priv::patch2cut(SurfacePatch &patch)
+{
+ CutMesh &mesh = patch.mesh;
+
+ std::string convert_map_name = "v:convert";
+ priv::ConvertMap convert_map = mesh.add_property_map(convert_map_name).first;
+
+ size_t indices_size = mesh.faces().size();
+ size_t vertices_size = mesh.vertices().size();
+
+ SurfaceCut sc;
+ sc.indices.reserve(indices_size);
+ sc.vertices.reserve(vertices_size);
+
+ std::vector v_cvt;
+ v_cvt.reserve(vertices_size);
+
+ for (VI vi : mesh.vertices()) {
+ // vi order is is not sorted
+ // assert(vi.idx() == sc.vertices.size());
+ // vi is not continous
+ // assert(vi.idx() < vertices_size);
+ convert_map[vi] = sc.vertices.size();
+ const P3 &p = mesh.point(vi);
+ sc.vertices.emplace_back(p.x(), p.y(), p.z());
+ }
+
+ for (FI fi : mesh.faces()) {
+ HI hi = mesh.halfedge(fi);
+ assert(mesh.next(hi).is_valid());
+ assert(mesh.next(mesh.next(hi)).is_valid());
+ // Is fi triangle?
+ assert(mesh.next(mesh.next(mesh.next(hi))) == hi);
+
+ // triangle indicies
+ Vec3i ti;
+ size_t i = 0;
+ for (VI vi : { mesh.source(hi),
+ mesh.target(hi),
+ mesh.target(mesh.next(hi))})
+ ti[i++] = convert_map[vi];
+ sc.indices.push_back(ti);
+ }
+ // Not neccessary, clean and free
+ mesh.remove_property_map(convert_map);
+ return sc;
+}
+
+SurfaceCut priv::merge_patches(SurfacePatches &patches, std::vector mask)
+{
+ SurfaceCut result;
+ for (SurfacePatch &patch : patches) {
+ size_t index = &patch - &patches.front();
+ if (!mask[index]) continue;
+ append(result, patch2cut(patch));
+ }
+ return result;
+}
+
SurfaceCuts priv::create_surface_cuts(const CutAOIs &cuts,
CutMesh &mesh,
const ReductionMap &reduction_map)
@@ -3592,11 +3813,17 @@ void priv::store(const std::vector &models,
}
void priv::store(const std::vector &models,
- const std::string &off_filename)
+ const std::string &dir)
{
+ prepare_dir(dir);
+ if (models.empty()) return;
+ if (models.size() == 1) {
+ CGAL::IO::write_OFF(dir + "model.off", models.front());
+ return;
+ }
size_t model_index = 0;
for (const priv::CutMesh& model : models) {
- std::string filename = off_filename + std::to_string(model_index++) + ".off";
+ std::string filename = dir + "model" + std::to_string(model_index++) + ".off";
CGAL::IO::write_OFF(filename, model);
}
}