Separate functionality
This commit is contained in:
parent
41e6dba3df
commit
87f22765ba
10 changed files with 887 additions and 276 deletions
|
@ -207,6 +207,30 @@ bool its_write_obj(const indexed_triangle_set &its, const char *file)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
FILE* fp = fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i)
|
||||
fprintf(fp, "v %f %f %f %f %f %f\n",
|
||||
its.vertices[i](0),
|
||||
its.vertices[i](1),
|
||||
its.vertices[i](2),
|
||||
color[i](0),
|
||||
color[i](1),
|
||||
color[i](2));
|
||||
for (size_t i = 0; i < its.indices.size(); ++i)
|
||||
fprintf(fp, "f %d %d %d\n",
|
||||
its.indices[i][0] + 1,
|
||||
its.indices[i][1] + 1,
|
||||
its.indices[i][2] + 1);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check validity of the mesh, assert on error.
|
||||
bool stl_validate(const stl_file *stl, const indexed_triangle_set &its)
|
||||
|
|
|
@ -303,6 +303,17 @@ extern bool its_write_obj(const indexed_triangle_set &its, const char *file);
|
|||
extern bool its_write_off(const indexed_triangle_set &its, const char *file);
|
||||
extern bool its_write_vrml(const indexed_triangle_set &its, const char *file);
|
||||
|
||||
|
||||
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> obj_color; // Vec3f
|
||||
/// <summary>
|
||||
/// write idexed triangle set into obj file with color
|
||||
/// </summary>
|
||||
/// <param name="its">input model</param>
|
||||
/// <param name="color">color of stored model</param>
|
||||
/// <param name="file">define place to store</param>
|
||||
/// <returns>True on success otherwise FALSE</returns>
|
||||
extern bool its_write_obj(const indexed_triangle_set& its, const std::vector<obj_color> &color, const char* file);
|
||||
|
||||
extern bool stl_write_dxf(stl_file *stl, const char *file, char *label);
|
||||
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
|
||||
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
|
||||
|
|
|
@ -33,6 +33,8 @@ set(SLIC3R_SOURCES
|
|||
Color.hpp
|
||||
Config.cpp
|
||||
Config.hpp
|
||||
CutSurface.cpp
|
||||
CutSurface.hpp
|
||||
EdgeGrid.cpp
|
||||
EdgeGrid.hpp
|
||||
ElephantFootCompensation.cpp
|
||||
|
|
587
src/libslic3r/CutSurface.cpp
Normal file
587
src/libslic3r/CutSurface.cpp
Normal file
|
@ -0,0 +1,587 @@
|
|||
#include "CutSurface.hpp"
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
#include <CGAL/Exact_integer.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Cartesian_converter.h>
|
||||
|
||||
// libslic3r
|
||||
#include "TriangleMesh.hpp" // its_merge
|
||||
#include "Utils.hpp" // next_highest_power_of_2
|
||||
|
||||
namespace priv {
|
||||
|
||||
namespace CGALProc = CGAL::Polygon_mesh_processing;
|
||||
namespace CGALParams = CGAL::Polygon_mesh_processing::parameters;
|
||||
|
||||
using EpicKernel = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using CutMesh = CGAL::Surface_mesh<EpicKernel::Point_3>;
|
||||
// using EpecKernel = CGAL::Exact_predicates_exact_constructions_kernel;
|
||||
// using CutMesh = CGAL::Surface_mesh<EpecKernel::Point_3>;
|
||||
|
||||
using DynamicEdgeProperty = CGAL::dynamic_edge_property_t<bool>;
|
||||
using SMPM = boost::property_map<priv::CutMesh, DynamicEdgeProperty>::SMPM;
|
||||
using EcmType = CGAL::internal::Dynamic<priv::CutMesh, SMPM>;
|
||||
|
||||
/// <summary>
|
||||
/// IntersectingElement
|
||||
///
|
||||
/// Adress polygon inside of ExPolygon
|
||||
/// Keep information about source of vertex:
|
||||
/// - from face (one of 2 possible)
|
||||
/// - from edge (one of 2 possible)
|
||||
///
|
||||
/// V1~~~~V2
|
||||
/// : f1 /|
|
||||
/// : / |
|
||||
/// : /e1|
|
||||
/// : / |e2
|
||||
/// :/ f2 |
|
||||
/// V1'~~~V2'
|
||||
///
|
||||
/// | .. edge
|
||||
/// / .. edge
|
||||
/// : .. foreign edge - neighbor
|
||||
/// ~ .. no care edge - idealy should not cross model
|
||||
/// V1,V1' .. projected 2d point to 3d
|
||||
/// V2,V2' .. projected 2d point to 3d
|
||||
///
|
||||
/// f1 .. text_face_1 (triangle face made by side of shape contour)
|
||||
/// f2 .. text_face_2
|
||||
/// e1 .. text_edge_1 (edge on side of face made by side of shape contour)
|
||||
/// e2 .. text_edge_2
|
||||
///
|
||||
/// </summary>
|
||||
struct IntersectingElement
|
||||
{
|
||||
// Base of the zero'th point of a contour in text mesh.
|
||||
// There are two vertices (front and rear) created for each contour,
|
||||
// thus there are 2x more vertices in text mesh than the number of contour points.
|
||||
// a.k.a offset of vertex inside vertices
|
||||
int32_t vertex_base{-1};
|
||||
|
||||
// index of point in Polygon contour
|
||||
int32_t point_index{-1};
|
||||
|
||||
// vertex or edge ID, where edge ID is the index of the source point.
|
||||
// There are 4 consecutive indices generated for a single contour edge:
|
||||
// 0th - 1st text edge (straight)
|
||||
// 1th - 1st text face
|
||||
// 2nd - 2nd text edge (diagonal)
|
||||
// 3th - 2nd text face
|
||||
// Type of intersecting element from extruded shape( 3d )
|
||||
enum class Type {
|
||||
edge_1 = 0,
|
||||
face_1 = 1,
|
||||
edge_2 = 2,
|
||||
face_2 = 3,
|
||||
|
||||
undefined = 4
|
||||
} type = Type::undefined;
|
||||
|
||||
// order of point in polygon for detect place between first and last point
|
||||
bool is_first{false};
|
||||
bool is_last{false};
|
||||
|
||||
IntersectingElement &set_type(Type t)
|
||||
{
|
||||
type = t;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Convert triangle mesh model to CGAL Surface_mesh
|
||||
/// Add property map for source face index
|
||||
/// </summary>
|
||||
/// <param name="its">Model</param>
|
||||
/// <returns>CGAL mesh - half edge mesh</returns>
|
||||
CutMesh to_cgal(const indexed_triangle_set &its);
|
||||
|
||||
/// <summary>
|
||||
/// Covert 2d shape (e.g. Glyph) to CGAL model
|
||||
/// </summary>
|
||||
/// <param name="shapes">2d shapes to project</param>
|
||||
/// <param name="projection">Define transformation 2d point into 3d</param>
|
||||
/// <param name="edge_shape_map_name">Name of property map to store conversion from edge to contour</param>
|
||||
/// <param name="face_shape_map_name">Name of property map to store conversion from face to contour</param>
|
||||
/// <returns>CGAL model of extruded shape</returns>
|
||||
CutMesh to_cgal(const Slic3r::ExPolygons &shapes,
|
||||
const Slic3r::Emboss::IProject &projection,
|
||||
const std::string &edge_shape_map_name,
|
||||
const std::string &face_shape_map_name);
|
||||
|
||||
enum class FaceType {
|
||||
// face inside of the cutted shape
|
||||
inside,
|
||||
// face outside of the cutted shape
|
||||
outside,
|
||||
// face without constrained edge (In or Out)
|
||||
not_constrained
|
||||
};
|
||||
|
||||
using FaceTypeMap = CutMesh::Property_map<CutMesh::Face_index, FaceType>;
|
||||
using VertexShapeMap = CutMesh::Property_map<CutMesh::Vertex_index, IntersectingElement>;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh to process</param>
|
||||
/// <param name="face_type_map">Output map with type of faces</param>
|
||||
/// <param name="vertex_shape_map">Keep information about source element of Face type</param>
|
||||
/// <param name="ecm"></param>
|
||||
/// <param name="project">projection of opoint</param>
|
||||
/// <param name="shape_mesh">Vertices of mesh made by shapes</param>
|
||||
void set_face_type(const CutMesh &mesh,
|
||||
FaceTypeMap &face_type_map,
|
||||
const VertexShapeMap &vertex_shape_map,
|
||||
const EcmType &ecm,
|
||||
const Slic3r::Emboss::IProject &project,
|
||||
const CutMesh &shape_mesh);
|
||||
|
||||
void flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map);
|
||||
|
||||
/// <summary>
|
||||
/// Debug purpose store of mesh with colored face by face type
|
||||
/// </summary>
|
||||
/// <param name="mesh">Input mesh, could add property color</param>
|
||||
/// <param name="face_type_map">Keep face type</param>
|
||||
/// <param name="file">File to store</param>
|
||||
void store(CutMesh &mesh, const FaceTypeMap &face_type_map, const std::string& file)
|
||||
{
|
||||
auto color_prop = mesh.property_map<priv::CutMesh::Face_index, CGAL::Color>("f:color");
|
||||
if (!color_prop.second)
|
||||
color_prop = mesh.add_property_map<priv::CutMesh::Face_index, CGAL::Color>("f:color");
|
||||
auto face_colors = color_prop.first;
|
||||
for (auto fi : mesh.faces()) {
|
||||
auto &color = face_colors[fi];
|
||||
switch (face_type_map[fi]) {
|
||||
case FaceType::inside: color = CGAL::Color{255, 0, 0}; break;
|
||||
case FaceType::outside: color = CGAL::Color{255, 0, 255}; break;
|
||||
case FaceType::not_constrained: color = CGAL::Color{0, 255, 0}; break;
|
||||
}
|
||||
}
|
||||
CGAL::IO::write_OFF(file, mesh);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Track source of intersection
|
||||
/// Anotate inner and outer face, not anotated face should be not not constrained
|
||||
/// </summary>
|
||||
struct Visitor {
|
||||
const CutMesh &object;
|
||||
const CutMesh &shape;
|
||||
|
||||
// Properties of the shape mesh:
|
||||
CutMesh::Property_map<CutMesh::Edge_index, IntersectingElement> edge_shape_map;
|
||||
CutMesh::Property_map<CutMesh::Face_index, IntersectingElement> face_shape_map;
|
||||
|
||||
// Properties of the object mesh.
|
||||
CutMesh::Property_map<CutMesh::Vertex_index, IntersectingElement> vert_shape_map;
|
||||
|
||||
using GT = boost::graph_traits<CutMesh>;
|
||||
using halfedge_descriptor = GT::halfedge_descriptor;
|
||||
|
||||
// keep source of intersection for each intersection
|
||||
std::vector<const IntersectingElement*> intersections;
|
||||
|
||||
/// <summary>
|
||||
/// Called when a new intersection point is detected.
|
||||
/// The intersection is detected using a face of tm_f and an edge of tm_e.
|
||||
/// Intersecting an edge hh_edge from tm_f with a face h_e of tm_e.
|
||||
/// https://doc.cgal.org/latest/Polygon_mesh_processing/classPMPCorefinementVisitor.html#a00ee0ca85db535a48726a92414acda7f
|
||||
/// </summary>
|
||||
/// <param name="i_id">The id of the intersection point, starting at 0. Ids are consecutive.</param>
|
||||
/// <param name="sdim">Dimension of a simplex part of face(h_e) that is intersected by hh_edge:
|
||||
/// 0 for vertex: target(h_e)
|
||||
/// 1 for edge: h_e
|
||||
/// 2 for the interior of face: face(h_e) </param>
|
||||
/// <param name="h_f">
|
||||
/// A halfedge from tm_f indicating the simplex intersected:
|
||||
/// if sdim==0 the target of h_f is the intersection point,
|
||||
/// if sdim==1 the edge of h_f contains the intersection point in its interior,
|
||||
/// if sdim==2 the face of h_f contains the intersection point in its interior.
|
||||
/// @Vojta: Edge of tm_f, see is_target_coplanar & is_source_coplanar whether any vertex of h_f is coplanar with face(h_e).
|
||||
/// </param>
|
||||
/// <param name="h_e">A halfedge from tm_e
|
||||
/// @Vojta: Vertex, halfedge or face of tm_e intersected by h_f, see comment at sdim.
|
||||
/// </param>
|
||||
/// <param name="tm_f">Mesh containing h_f</param>
|
||||
/// <param name="tm_e">Mesh containing h_e</param>
|
||||
/// <param name="is_target_coplanar">True if the target of h_e is the intersection point
|
||||
/// @Vojta: source(h_f) is coplanar with face(made by h_e).</param>
|
||||
/// <param name="is_source_coplanar">True if the source of h_e is the intersection point
|
||||
/// @Vojta: target(h_f) is coplanar with face(h_e).</param>
|
||||
void intersection_point_detected(std::size_t i_id,
|
||||
int sdim,
|
||||
halfedge_descriptor h_f,
|
||||
halfedge_descriptor h_e,
|
||||
const CutMesh &tm_f,
|
||||
const CutMesh &tm_e,
|
||||
bool is_target_coplanar,
|
||||
bool is_source_coplanar)
|
||||
{
|
||||
if (i_id <= intersections.size()) {
|
||||
intersections.reserve(Slic3r::next_highest_power_of_2(i_id + 1));
|
||||
intersections.resize(i_id + 1);
|
||||
}
|
||||
|
||||
const IntersectingElement* intersection_ptr = nullptr;
|
||||
if (&tm_e == &shape) {
|
||||
assert(&tm_f == &object);
|
||||
switch (sdim) {
|
||||
case 1:
|
||||
// edge x edge intersection
|
||||
intersection_ptr = &edge_shape_map[shape.edge(h_e)];
|
||||
break;
|
||||
case 2:
|
||||
// edge x face intersection
|
||||
intersection_ptr = &face_shape_map[shape.face(h_e)];
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
if (is_target_coplanar)
|
||||
vert_shape_map[object.source(h_f)] = *intersection_ptr;
|
||||
if (is_source_coplanar)
|
||||
vert_shape_map[object.target(h_f)] = *intersection_ptr;
|
||||
} else {
|
||||
assert(&tm_f == &shape && &tm_e == &object);
|
||||
assert(!is_target_coplanar);
|
||||
assert(!is_source_coplanar);
|
||||
intersection_ptr = &edge_shape_map[shape.edge(h_f)];
|
||||
if (sdim == 0)
|
||||
vert_shape_map[object.target(h_e)] = *intersection_ptr;
|
||||
}
|
||||
intersections[i_id] = intersection_ptr;
|
||||
}
|
||||
|
||||
using vertex_descriptor = GT::vertex_descriptor;
|
||||
void new_vertex_added(std::size_t node_id, vertex_descriptor vh, const CutMesh &tm)
|
||||
{
|
||||
assert(&tm == &object);
|
||||
assert(node_id < intersections.size());
|
||||
const IntersectingElement * intersection_ptr = intersections[node_id];
|
||||
assert(intersection_ptr != nullptr);
|
||||
assert(intersection_ptr->point_index != -1);
|
||||
vert_shape_map[vh] = *intersection_ptr; // copy ?!?
|
||||
}
|
||||
|
||||
using face_descriptor = GT::face_descriptor;
|
||||
void before_subface_creations(face_descriptor /* f_old */, CutMesh &/* mesh */){}
|
||||
void after_subface_created(face_descriptor /* f_new */, CutMesh &/* mesh */) {}
|
||||
void after_subface_creations(CutMesh&) {}
|
||||
void before_subface_created(CutMesh&) {}
|
||||
void before_edge_split(halfedge_descriptor /* h */, CutMesh& /* tm */) {}
|
||||
void edge_split(halfedge_descriptor /* hnew */, CutMesh& /* tm */) {}
|
||||
void after_edge_split() {}
|
||||
void add_retriangulation_edge(halfedge_descriptor /* h */, CutMesh& /* tm */) {}
|
||||
};
|
||||
} // namespace privat
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
void Slic3r::append(SurfaceCut &sc, SurfaceCut &&sc_add)
|
||||
{
|
||||
if (sc.empty()) {
|
||||
sc = std::move(sc_add);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sc_add.cut.empty()) {
|
||||
SurfaceCut::Index offset = static_cast<SurfaceCut::Index>(
|
||||
sc.vertices.size());
|
||||
size_t require = sc.cut.size() + sc_add.cut.size();
|
||||
if (sc.cut.capacity() < require) sc.cut.reserve(require);
|
||||
for (std::vector<SurfaceCut::Index> &cut : sc_add.cut)
|
||||
for (SurfaceCut::Index &i : cut) i += offset;
|
||||
append(sc.cut, std::move(sc_add.cut));
|
||||
}
|
||||
its_merge(sc, std::move(sc_add));
|
||||
}
|
||||
|
||||
SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model,
|
||||
const ExPolygons &shapes,
|
||||
const Emboss::IProject &projection)
|
||||
{
|
||||
priv::CutMesh cgal_model = priv::to_cgal(model);
|
||||
|
||||
std::string edge_shape_map_name = "e:IntersectingElement";
|
||||
std::string face_shape_map_name = "f:IntersectingElement";
|
||||
priv::CutMesh cgal_shape = priv::to_cgal(shapes, projection, edge_shape_map_name, face_shape_map_name);
|
||||
|
||||
auto& edge_shape_map = cgal_shape.property_map<priv::CutMesh::Edge_index, priv::IntersectingElement>(edge_shape_map_name).first;
|
||||
auto& face_shape_map = cgal_shape.property_map<priv::CutMesh::Face_index, priv::IntersectingElement>(face_shape_map_name).first;
|
||||
|
||||
std::string vert_shape_map_name = "v:IntersectingElement";
|
||||
auto& vert_shape_map = cgal_model.add_property_map<priv::CutMesh::Vertex_index, priv::IntersectingElement>(vert_shape_map_name).first;
|
||||
|
||||
priv::Visitor visitor{cgal_model, cgal_shape, edge_shape_map, face_shape_map, vert_shape_map};
|
||||
|
||||
// bool map for affected edge
|
||||
priv::EcmType ecm = get(priv::DynamicEdgeProperty(), cgal_model);
|
||||
|
||||
const auto& p = CGAL::Polygon_mesh_processing::parameters::throw_on_self_intersection(false).visitor(visitor).edge_is_constrained_map(ecm);
|
||||
const auto& q = CGAL::Polygon_mesh_processing::parameters::do_not_modify(true);
|
||||
|
||||
CGAL::Polygon_mesh_processing::corefine(cgal_model, cgal_shape, p, q);
|
||||
|
||||
std::string face_type_map_name = "f:side";
|
||||
priv::FaceTypeMap face_type_map = cgal_model.add_property_map<priv::CutMesh::Face_index, priv::FaceType>(face_type_map_name).first;
|
||||
|
||||
// Select inside and outside face in model
|
||||
priv::set_face_type(cgal_model, face_type_map, vert_shape_map, ecm, projection, cgal_shape);
|
||||
priv::store(cgal_model, face_type_map, "C:/data/temp/constrained.off"); // only debug
|
||||
|
||||
// Seed fill the other faces inside the region.
|
||||
priv::flood_fill_inner(cgal_model, face_type_map);
|
||||
priv::store(cgal_model, face_type_map, "C:/data/temp/filled.off"); // only debug
|
||||
|
||||
SurfaceCut result;
|
||||
// for (const ExPolygon& shape : shapes)
|
||||
// append(result, cut_surface(model, shape, projection));
|
||||
return result;
|
||||
}
|
||||
|
||||
priv::CutMesh priv::to_cgal(const indexed_triangle_set &its)
|
||||
{
|
||||
CutMesh result;
|
||||
if (its.empty()) return result;
|
||||
|
||||
const std::vector<stl_vertex> &vertices = its.vertices;
|
||||
const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
|
||||
|
||||
size_t vertices_count = vertices.size();
|
||||
size_t faces_count = indices.size();
|
||||
size_t edges_count = (faces_count * 3) / 2;
|
||||
result.reserve(vertices_count, edges_count, faces_count);
|
||||
|
||||
for (const stl_vertex &v : vertices)
|
||||
result.add_vertex(CutMesh::Point{v.x(), v.y(), v.z()});
|
||||
|
||||
using VI = CutMesh::Vertex_index;
|
||||
for (const stl_triangle_vertex_indices &f : indices)
|
||||
result.add_face(static_cast<VI>(f[0]),
|
||||
static_cast<VI>(f[1]),
|
||||
static_cast<VI>(f[2]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
priv::CutMesh priv::to_cgal(const Slic3r::ExPolygons &shapes,
|
||||
const Slic3r::Emboss::IProject &projection,
|
||||
const std::string &edge_shape_map_name,
|
||||
const std::string &face_shape_map_name)
|
||||
{
|
||||
CutMesh result;
|
||||
if (shapes.empty()) return result;
|
||||
|
||||
auto edge_shape_map = result.add_property_map<CutMesh::Edge_index, IntersectingElement>(edge_shape_map_name).first;
|
||||
auto face_shape_map = result.add_property_map<CutMesh::Face_index, IntersectingElement>(face_shape_map_name).first;
|
||||
|
||||
std::vector<CutMesh::Vertex_index> indices;
|
||||
auto insert_contour = [&projection, &indices, &result,
|
||||
&edge_shape_map, &face_shape_map]
|
||||
(const Polygon &polygon) {
|
||||
indices.clear();
|
||||
indices.reserve(polygon.points.size() * 2);
|
||||
size_t num_vertices_old = result.number_of_vertices();
|
||||
for (const Point &p2 : polygon.points) {
|
||||
auto p = projection.project(p2);
|
||||
CutMesh::Point v_first{p.first.x(), p.first.y(), p.first.z()};
|
||||
CutMesh::Point v_second{p.second.x(), p.second.y(), p.second.z()};
|
||||
|
||||
CutMesh::Vertex_index vi = result.add_vertex(v_first);
|
||||
assert(size_t(vi) == (indices.size() + num_vertices_old));
|
||||
indices.emplace_back(vi);
|
||||
|
||||
vi = result.add_vertex(v_second);
|
||||
assert(size_t(vi) == (indices.size() + num_vertices_old));
|
||||
indices.emplace_back(vi);
|
||||
}
|
||||
|
||||
auto find_edge = [&result](CutMesh::Face_index fi,
|
||||
CutMesh::Vertex_index from,
|
||||
CutMesh::Vertex_index to) {
|
||||
CutMesh::Halfedge_index hi = result.halfedge(fi);
|
||||
for (; result.target(hi) != to; hi = result.next(hi));
|
||||
assert(result.source(hi) == from);
|
||||
assert(result.target(hi) == to);
|
||||
return result.edge(hi);
|
||||
};
|
||||
|
||||
int32_t contour_index = 0;
|
||||
for (int32_t i = 0; i < int32_t(indices.size()); i += 2) {
|
||||
bool is_first = i == 0;
|
||||
bool is_last = (i + 2) >= indices.size();
|
||||
int32_t j = is_last ? 0 : (i + 2);
|
||||
|
||||
auto fi1 = result.add_face(indices[i], indices[i + 1], indices[j]);
|
||||
auto ei1 = find_edge(fi1, indices[i], indices[i + 1]);
|
||||
auto ei2 = find_edge(fi1, indices[i + 1], indices[j]);
|
||||
auto fi2 = result.add_face(indices[j], indices[i + 1], indices[j + 1]);
|
||||
IntersectingElement element {num_vertices_old, contour_index, IntersectingElement::Type::undefined, is_first, is_last};
|
||||
edge_shape_map[ei1] = element.set_type(IntersectingElement::Type::edge_1);
|
||||
face_shape_map[fi1] = element.set_type(IntersectingElement::Type::face_1);
|
||||
edge_shape_map[ei2] = element.set_type(IntersectingElement::Type::edge_2);
|
||||
face_shape_map[fi2] = element.set_type(IntersectingElement::Type::face_2);
|
||||
++contour_index;
|
||||
}
|
||||
};
|
||||
|
||||
size_t count_point = count_points(shapes);
|
||||
result.reserve(result.number_of_vertices() + 2 * count_point,
|
||||
result.number_of_edges() + 4 * count_point,
|
||||
result.number_of_faces() + 2 * count_point);
|
||||
|
||||
// Identify polygon
|
||||
for (const ExPolygon &shape : shapes) {
|
||||
insert_contour(shape.contour);
|
||||
for (const Polygon &hole : shape.holes)
|
||||
insert_contour(hole);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void priv::set_face_type(const CutMesh &mesh,
|
||||
FaceTypeMap &face_type_map,
|
||||
const VertexShapeMap &vertex_shape_map,
|
||||
const EcmType &ecm,
|
||||
const Emboss::IProject &project,
|
||||
const CutMesh &shape_mesh)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (auto& fi : mesh.faces()) {
|
||||
FaceType face_type = FaceType::not_constrained;
|
||||
auto hi_end = mesh.halfedge(fi);
|
||||
auto hi = hi_end;
|
||||
|
||||
do {
|
||||
CGAL::SM_Edge_index edge_index = mesh.edge(hi);
|
||||
// is edge new created - constrained?
|
||||
if (get(ecm, edge_index)) {
|
||||
// This face has a constrained edge.
|
||||
IntersectingElement shape_from = vertex_shape_map[mesh.source(hi)];
|
||||
IntersectingElement shape_to = vertex_shape_map[mesh.target(hi)];
|
||||
|
||||
assert(shape_from.point_index != -1);
|
||||
assert(shape_from.type != IntersectingElement::Type::undefined);
|
||||
assert(shape_to.point_index != -1);
|
||||
assert(shape_to.type != IntersectingElement::Type::undefined);
|
||||
|
||||
// assert mean: There is constrained between two shapes
|
||||
// Filip think it can't happens.
|
||||
// consider what to do?
|
||||
assert(shape_from.vertex_base == shape_to.vertex_base);
|
||||
|
||||
bool is_inside = false;
|
||||
|
||||
// index into contour
|
||||
int32_t i_from = shape_from.point_index;
|
||||
int32_t i_to = shape_to.point_index;
|
||||
if (i_from == i_to && shape_from.type == shape_to.type) {
|
||||
// intersecting element must be face
|
||||
assert(shape_from.type == IntersectingElement::Type::face_1 ||
|
||||
shape_from.type == IntersectingElement::Type::face_2);
|
||||
|
||||
// count of vertices is twice as count of point in the contour
|
||||
int i = i_from * 2;
|
||||
// j is next contour point in vertices
|
||||
int j = shape_from.is_last ? 0 : i + 2;
|
||||
i += shape_from.vertex_base;
|
||||
j += shape_from.vertex_base;
|
||||
|
||||
// opposit point(in triangle face) to edge
|
||||
const auto &p = mesh.point(mesh.target(mesh.next(hi)));
|
||||
|
||||
// abc is source triangle face
|
||||
auto abcp =
|
||||
shape_from.type == IntersectingElement::Type::face_1 ?
|
||||
CGAL::orientation(
|
||||
shape_mesh.point(CGAL::SM_Vertex_index(i)),
|
||||
shape_mesh.point(CGAL::SM_Vertex_index(i + 1)),
|
||||
shape_mesh.point(CGAL::SM_Vertex_index(j)), p) :
|
||||
//shape_from.type == IntersectingElement::Type::face_2
|
||||
CGAL::orientation(
|
||||
shape_mesh.point(CGAL::SM_Vertex_index(j)),
|
||||
shape_mesh.point(CGAL::SM_Vertex_index(i + 1)),
|
||||
shape_mesh.point(CGAL::SM_Vertex_index(j + 1)), p);
|
||||
is_inside = abcp == CGAL::POSITIVE;
|
||||
} else if (i_from < i_to || (i_from == i_to && shape_from.type < shape_to.type)) {
|
||||
// TODO: check that it is continous indices of contour
|
||||
bool is_last = shape_from.is_first && shape_to.is_last;
|
||||
if (!is_last) is_inside = true;
|
||||
} else { // i_from > i_to || (i_from == i_to && shape_from.type > shape_to.type)
|
||||
// TODO: check that it is continous indices of contour
|
||||
bool is_last = shape_to.is_first && shape_from.is_last;
|
||||
if (is_last) is_inside = true;
|
||||
}
|
||||
|
||||
if (is_inside) {
|
||||
// Is this face oriented towards p or away from p?
|
||||
const auto &a = mesh.point(mesh.source(hi));
|
||||
const auto &b = mesh.point(mesh.target(hi));
|
||||
const auto &c = mesh.point(mesh.target(mesh.next(hi)));
|
||||
|
||||
Vec3f a_(a.x(), a.y(), a.z());
|
||||
Vec3f p_ = project.project(a_);
|
||||
CGAL::Epick::Point_3 p{p_.x(), p_.y(), p_.z()};
|
||||
auto abcp = CGAL::orientation(a, b, c, p);
|
||||
if (abcp == CGAL::POSITIVE)
|
||||
face_type = FaceType::inside;
|
||||
else
|
||||
is_inside = false;
|
||||
}
|
||||
if (!is_inside) face_type = FaceType::outside;
|
||||
break;
|
||||
}
|
||||
// next half edge index inside of face
|
||||
hi = mesh.next(hi);
|
||||
} while (hi != hi_end);
|
||||
face_type_map[fi] = face_type;
|
||||
}
|
||||
}
|
||||
|
||||
void priv::flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map)
|
||||
{
|
||||
for (Visitor::face_descriptor fi : mesh.faces()) {
|
||||
if (face_type_map[fi] != FaceType::not_constrained) continue;
|
||||
|
||||
// check if neighbor face is inside
|
||||
Visitor::halfedge_descriptor hi = mesh.halfedge(fi);
|
||||
Visitor::halfedge_descriptor hi_end = hi;
|
||||
|
||||
bool has_inside_neighbor = false;
|
||||
std::vector<CutMesh::Face_index> queue;
|
||||
do {
|
||||
Visitor::face_descriptor fi_opposite = mesh.face(mesh.opposite(hi));
|
||||
FaceType side = face_type_map[fi_opposite];
|
||||
if (side == FaceType::inside) {
|
||||
has_inside_neighbor = true;
|
||||
} else if (side == FaceType::not_constrained) {
|
||||
queue.emplace_back(fi_opposite);
|
||||
}
|
||||
hi = mesh.next(hi);
|
||||
} while (hi != hi_end);
|
||||
if (!has_inside_neighbor) continue;
|
||||
face_type_map[fi] = FaceType::inside;
|
||||
while (!queue.empty()) {
|
||||
Visitor::face_descriptor fi = queue.back();
|
||||
queue.pop_back();
|
||||
// Do not fill twice
|
||||
if (face_type_map[fi] == FaceType::inside) continue;
|
||||
face_type_map[fi] = FaceType::inside;
|
||||
|
||||
// check neighbor triangle
|
||||
Visitor::halfedge_descriptor hi = mesh.halfedge(fi);
|
||||
Visitor::halfedge_descriptor hi_end = hi;
|
||||
do {
|
||||
Visitor::face_descriptor fi_opposite = mesh.face(mesh.opposite(hi));
|
||||
FaceType side = face_type_map[fi_opposite];
|
||||
if (side == FaceType::not_constrained)
|
||||
queue.emplace_back(fi_opposite);
|
||||
hi = mesh.next(hi);
|
||||
} while (hi != hi_end);
|
||||
}
|
||||
}
|
||||
}
|
48
src/libslic3r/CutSurface.hpp
Normal file
48
src/libslic3r/CutSurface.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef slic3r_CutSurface_hpp_
|
||||
#define slic3r_CutSurface_hpp_
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <admesh/stl.h> // indexed_triangle_set
|
||||
#include "Polygon.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Emboss.hpp"
|
||||
|
||||
namespace Slic3r{
|
||||
|
||||
/// <summary>
|
||||
/// Represents cutted surface from object
|
||||
/// Extend index triangle set by outlines
|
||||
/// </summary>
|
||||
struct SurfaceCut : public indexed_triangle_set
|
||||
{
|
||||
using Index = unsigned int;
|
||||
// cutted surface
|
||||
indexed_triangle_set mesh;
|
||||
|
||||
// list of circulated open surface
|
||||
std::vector<std::vector<Index>> cut;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Merge two surface cuts together
|
||||
/// Added surface cut will be consumed
|
||||
/// </summary>
|
||||
/// <param name="sc">Surface cut to extend</param>
|
||||
/// <param name="sc_add">Surface cut to consume</param>
|
||||
void append(SurfaceCut &sc, SurfaceCut &&sc_add);
|
||||
|
||||
/// <summary>
|
||||
/// Cut surface shape from model
|
||||
/// </summary>
|
||||
/// <param name="model">Mesh to cut</param>
|
||||
/// <param name="shapes">Multi shapes to cut from model</param>
|
||||
/// <param name="projection">Define transformation from 2d shape to 3d</param>
|
||||
/// <returns>Cutted surface from model</returns>
|
||||
SurfaceCut cut_surface(const indexed_triangle_set &model,
|
||||
const ExPolygons &shapes,
|
||||
const Emboss::IProject &projection);
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_CutSurface_hpp_
|
|
@ -756,9 +756,14 @@ std::pair<Vec3f, Vec3f> Emboss::ProjectZ::project(const Point &p) const
|
|||
static_cast<float>(p.x() * SHAPE_SCALE),
|
||||
static_cast<float>(p.y() * SHAPE_SCALE),
|
||||
0.f);
|
||||
Vec3f back = front; // copy
|
||||
back.z() = m_depth;
|
||||
return std::make_pair(front, back);
|
||||
return std::make_pair(front, project(front));
|
||||
}
|
||||
|
||||
Vec3f Emboss::ProjectZ::project(const Vec3f &point) const
|
||||
{
|
||||
Vec3f res = point; // copy
|
||||
res.z() = m_depth;
|
||||
return res;
|
||||
}
|
||||
|
||||
Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
|
||||
|
|
|
@ -187,6 +187,15 @@ public:
|
|||
/// second - back spatial point
|
||||
/// </returns>
|
||||
virtual std::pair<Vec3f, Vec3f> project(const Point &p) const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Move point with respect to projection direction
|
||||
/// e.g. Orthogonal projection will move with point by direction
|
||||
/// e.g. Spherical projection need to use center of projection
|
||||
/// </summary>
|
||||
/// <param name="point">Spatial point coordinate</param>
|
||||
/// <returns>Projected spatial point</returns>
|
||||
virtual Vec3f project(const Vec3f &point) const = 0;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -212,27 +221,30 @@ public:
|
|||
public:
|
||||
ProjectZ(float depth) : m_depth(depth) {}
|
||||
// Inherited via IProject
|
||||
virtual std::pair<Vec3f, Vec3f> project(const Point &p) const override;
|
||||
std::pair<Vec3f, Vec3f> project(const Point &p) const override;
|
||||
Vec3f project(const Vec3f &point) const override;
|
||||
float m_depth;
|
||||
};
|
||||
|
||||
class ProjectScale : public IProject
|
||||
{
|
||||
std::unique_ptr<IProject> core;
|
||||
float m_scale;
|
||||
public:
|
||||
ProjectScale(std::unique_ptr<IProject> core, float scale)
|
||||
: m_scale(scale)
|
||||
, core(std::move(core))
|
||||
: core(std::move(core)), m_scale(scale)
|
||||
{}
|
||||
|
||||
// Inherited via IProject
|
||||
virtual std::pair<Vec3f, Vec3f> project(const Point &p) const override
|
||||
std::pair<Vec3f, Vec3f> project(const Point &p) const override
|
||||
{
|
||||
auto res = core->project(p);
|
||||
return std::make_pair(res.first * m_scale, res.second * m_scale);
|
||||
}
|
||||
Vec3f project(const Vec3f &point) const override{
|
||||
return core->project(point);
|
||||
}
|
||||
|
||||
float m_scale;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1143,6 +1143,23 @@ void its_reverse_all_facets(indexed_triangle_set &its)
|
|||
std::swap(face[0], face[1]);
|
||||
}
|
||||
|
||||
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add)
|
||||
{
|
||||
if (its.empty()) {
|
||||
its = std::move(its_add);
|
||||
return;
|
||||
}
|
||||
auto &verts = its.vertices;
|
||||
size_t verts_size = verts.size();
|
||||
Slic3r::append(verts, std::move(its_add.vertices));
|
||||
|
||||
// increase face indices
|
||||
int offset = static_cast<int>(verts_size);
|
||||
for (auto &face : its_add.indices)
|
||||
for (int i = 0; i < 3; ++i) face[i] += offset;
|
||||
Slic3r::append(its.indices, std::move(its_add.indices));
|
||||
}
|
||||
|
||||
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B)
|
||||
{
|
||||
auto N = int(A.vertices.size());
|
||||
|
|
|
@ -286,6 +286,14 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its,
|
|||
float its_volume(const indexed_triangle_set &its);
|
||||
float its_average_edge_length(const indexed_triangle_set &its);
|
||||
|
||||
/// <summary>
|
||||
/// Merge one triangle mesh to another
|
||||
/// Added triangle set will be consumed
|
||||
/// </summary>
|
||||
/// <param name="its">IN/OUT triangle mesh</param>
|
||||
/// <param name="its_add">Triangle mesh (will be consumed)</param>
|
||||
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add);
|
||||
|
||||
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
|
||||
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
|
||||
void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
|
||||
|
|
|
@ -216,6 +216,35 @@ TEST_CASE("triangle intersection", "[]")
|
|||
CHECK(abs(i.y() - 1.) < std::numeric_limits<double>::epsilon());
|
||||
}
|
||||
|
||||
#include "libslic3r/CutSurface.hpp"
|
||||
TEST_CASE("Cut surface", "[]")
|
||||
{
|
||||
std::string font_path = get_font_filepath();
|
||||
char letter = '%';
|
||||
float flatness = 2.;
|
||||
|
||||
auto font = Emboss::create_font_file(font_path.c_str());
|
||||
REQUIRE(font != nullptr);
|
||||
|
||||
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter, flatness);
|
||||
REQUIRE(glyph.has_value());
|
||||
|
||||
ExPolygons shape = glyph->shape;
|
||||
REQUIRE(!shape.empty());
|
||||
|
||||
float z_depth = 50.f;
|
||||
Emboss::ProjectZ projection(z_depth);
|
||||
|
||||
auto object = its_make_cube(782 - 49 + 50, 724 + 10 + 50, 5);
|
||||
its_translate(object, Vec3f(49 - 25, -10 - 25, 2.5));
|
||||
auto cube2 = object; // copy
|
||||
its_translate(cube2, Vec3f(100, -40, 40));
|
||||
its_merge(object, std::move(cube2));
|
||||
|
||||
auto surface = cut_surface(object, shape, projection);
|
||||
//its_write_obj(surface, "C:/data/temp/surface_cutted.obj");
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
@ -323,7 +352,7 @@ struct IntersectingElemnt
|
|||
{
|
||||
// Index into vector of ShapeVertexId
|
||||
// describe point on shape contour
|
||||
int32_t vertex_index{ -1 };
|
||||
int32_t vertex_index{-1};
|
||||
|
||||
// index of point in Polygon contour
|
||||
int32_t point_index{-1};
|
||||
|
@ -356,34 +385,6 @@ namespace Slic3r::MeshBoolean::cgal2 {
|
|||
// using _EpecMesh = CGAL::Surface_mesh<EpecKernel::Point_3>;
|
||||
|
||||
using CGALMesh = _EpicMesh;
|
||||
|
||||
// Add an indexed triangle mesh to CGAL Surface_mesh.
|
||||
// Store map of CGAL face to source face index into face_map.
|
||||
void triangle_mesh_to_cgal(
|
||||
const std::vector<stl_vertex> &V,
|
||||
const std::vector<stl_triangle_vertex_indices> &F,
|
||||
CGALMesh &out,
|
||||
CGALMesh::Property_map<CGAL::SM_Face_index, int32_t> object_face_source_id)
|
||||
{
|
||||
if (F.empty())
|
||||
return;
|
||||
|
||||
size_t vertices_count = V.size();
|
||||
size_t edges_count = (F.size() * 3) / 2;
|
||||
size_t faces_count = F.size();
|
||||
out.reserve(vertices_count, edges_count, faces_count);
|
||||
|
||||
for (auto& v : V)
|
||||
out.add_vertex(typename CGALMesh::Point{ v.x(), v.y(), v.z() });
|
||||
|
||||
using VI = typename CGALMesh::Vertex_index;
|
||||
for (auto& f : F) {
|
||||
auto fid = out.add_face(VI(f(0)), VI(f(1)), VI(f(2)));
|
||||
// index of face in source triangle mesh
|
||||
int32_t index = static_cast<int32_t>(&f - &F.front());
|
||||
object_face_source_id[fid] = index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert triangle mesh model to CGAL Surface_mesh
|
||||
|
@ -434,7 +435,7 @@ namespace Slic3r::MeshBoolean::cgal2 {
|
|||
/// <param name="face_shape_map_name">Name of property map to store conversion from face to contour</param>
|
||||
/// <param name="contour_indices">Identify point on shape contour</param>
|
||||
/// <returns>CGAL model of extruded shape</returns>
|
||||
CGALMesh to_cgal(const ExPolygon &shape,
|
||||
CGALMesh to_cgal(const ExPolygons &shape,
|
||||
const Slic3r::Emboss::IProject &projection,
|
||||
int32_t shape_id,
|
||||
const std::string &edge_shape_map_name,
|
||||
|
@ -491,83 +492,17 @@ namespace Slic3r::MeshBoolean::cgal2 {
|
|||
|
||||
// Identify polygon
|
||||
// (contour_id > 0) are holes
|
||||
size_t contour_id = 0;
|
||||
insert_contour(shape.contour, shape_id, contour_id++);
|
||||
for (const Polygon& hole : shape.holes)
|
||||
insert_contour(hole, shape_id, contour_id++);
|
||||
|
||||
for (const auto &s : shape) {
|
||||
size_t contour_id = 0;
|
||||
insert_contour(s.contour, shape_id, contour_id++);
|
||||
for (const Polygon &hole : s.holes)
|
||||
insert_contour(hole, shape_id, contour_id++);
|
||||
++shape_id;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool its_write_obj(const indexed_triangle_set& its, const std::vector<Vec3f> &color, const char* file)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
FILE* fp = fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i)
|
||||
fprintf(fp, "v %f %f %f %f %f %f\n",
|
||||
its.vertices[i](0), its.vertices[i](1), its.vertices[i](2),
|
||||
color[i](0), color[i](1), color[i](2));
|
||||
for (size_t i = 0; i < its.indices.size(); ++i)
|
||||
fprintf(fp, "f %d %d %d\n", its.indices[i][0] + 1, its.indices[i][1] + 1, its.indices[i][2] + 1);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge one triangle mesh to another
|
||||
/// Added triangle set will allive
|
||||
/// </summary>
|
||||
/// <param name="its">IN / OUT triangle mesh</param>
|
||||
/// <param name="its_add">IN triangle mesh</param>
|
||||
void its_append(indexed_triangle_set &its, const indexed_triangle_set &its_add)
|
||||
{
|
||||
if (its.empty()) {
|
||||
its = its_add; // copy
|
||||
return;
|
||||
}
|
||||
auto &verts = its.vertices;
|
||||
size_t verts_size = verts.size();
|
||||
verts.reserve(verts_size + its_add.vertices.size());
|
||||
append(verts, its_add.vertices);
|
||||
|
||||
auto &idxs = its.indices;
|
||||
idxs.reserve(idxs.size() + its_add.indices.size());
|
||||
|
||||
// increase face indices
|
||||
int offset = static_cast<int>(verts_size);
|
||||
for (auto face : its_add.indices) {
|
||||
for (int i = 0; i < 3; ++i) face[i] += offset;
|
||||
idxs.emplace_back(face);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge one triangle mesh to another
|
||||
/// Added triangle set will be consumed
|
||||
/// </summary>
|
||||
/// <param name="its">IN/OUT triangle mesh</param>
|
||||
/// <param name="its_add">Triangle mesh (will be consumed)</param>
|
||||
void its_append(indexed_triangle_set &its, indexed_triangle_set &&its_add)
|
||||
{
|
||||
if (its.empty()) {
|
||||
its = std::move(its_add);
|
||||
return;
|
||||
}
|
||||
auto &verts = its.vertices;
|
||||
size_t verts_size = verts.size();
|
||||
append(verts, std::move(its_add.vertices));
|
||||
|
||||
// increase face indices
|
||||
int offset = static_cast<int>(verts_size);
|
||||
for (auto &face : its_add.indices)
|
||||
for (int i = 0; i < 3; ++i) face[i] += offset;
|
||||
append(its.indices, std::move(its_add.indices));
|
||||
}
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
|
||||
//// 1 ////
|
||||
|
||||
|
@ -609,79 +544,8 @@ indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents cutted surface from object
|
||||
/// Extend index triangle set by outlines
|
||||
/// </summary>
|
||||
struct SurfaceCut : public indexed_triangle_set
|
||||
{
|
||||
using Index = unsigned int;
|
||||
// cutted surface
|
||||
indexed_triangle_set mesh;
|
||||
|
||||
// list of circulated open surface
|
||||
std::vector<std::vector<Index>> cut;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Merge two surface cuts together
|
||||
/// Added surface cut will be consumed
|
||||
/// </summary>
|
||||
/// <param name="sc">Surface cut to extend</param>
|
||||
/// <param name="sc_add">Surface cut to consume</param>
|
||||
void append(SurfaceCut &sc, SurfaceCut &&sc_add)
|
||||
{
|
||||
if (sc.empty()) {
|
||||
sc = std::move(sc_add);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sc_add.cut.empty()) {
|
||||
SurfaceCut::Index offset =
|
||||
static_cast<SurfaceCut::Index>(sc.vertices.size());
|
||||
size_t require = sc.cut.size() + sc_add.cut.size();
|
||||
if (sc.cut.capacity() < require) sc.cut.reserve(require);
|
||||
for (std::vector<SurfaceCut::Index> &cut : sc_add.cut)
|
||||
for (SurfaceCut::Index &i : cut) i += offset;
|
||||
append(sc.cut, std::move(sc_add.cut));
|
||||
}
|
||||
its_append(sc, std::move(sc_add));
|
||||
}
|
||||
|
||||
using MyMesh = Slic3r::MeshBoolean::cgal2::CGALMesh;
|
||||
|
||||
/// <summary>
|
||||
/// Cut surface shape from model
|
||||
/// </summary>
|
||||
/// <param name="model">Mesh to cut</param>
|
||||
/// <param name="shape">Shape to cut from model</param>
|
||||
/// <param name="projection">Define transformation from 2d shape to 3d</param>
|
||||
/// <returns>Cutted surface from model</returns>
|
||||
SurfaceCut cut_surface(const MyMesh &model,
|
||||
const ExPolygon &shape,
|
||||
const Emboss::IProject &projection)
|
||||
{
|
||||
SurfaceCut result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cut surface shape from model
|
||||
/// </summary>
|
||||
/// <param name="model">Mesh to cut</param>
|
||||
/// <param name="shapes">Multi shapes to cut from model</param>
|
||||
/// <param name="projection">Define transformation from 2d shape to 3d</param>
|
||||
/// <returns>Cutted surface from model</returns>
|
||||
SurfaceCut cut_surface(const indexed_triangle_set &model,
|
||||
const ExPolygons &shapes,
|
||||
const Emboss::IProject &projection)
|
||||
{
|
||||
SurfaceCut result;
|
||||
//for (const ExPolygon& shape : shapes)
|
||||
// append(result, cut_surface(model, shape, projection));
|
||||
return result;
|
||||
}
|
||||
|
||||
// First Idea //// 1 ////
|
||||
// Use source model to modify ONLY surface of text ModelVolume
|
||||
|
||||
|
@ -691,7 +555,7 @@ SurfaceCut cut_surface(const indexed_triangle_set &model,
|
|||
TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
||||
{
|
||||
std::string font_path = get_font_filepath();
|
||||
char letter = '$';
|
||||
char letter = '%';
|
||||
float flatness = 2.;
|
||||
|
||||
auto font = Emboss::create_font_file(font_path.c_str());
|
||||
|
@ -719,7 +583,7 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
auto cube2 = cube;
|
||||
// its_translate(cube2, Vec3f(0, 0, 40));
|
||||
its_translate(cube2, Vec3f(100, -40, 40));
|
||||
its_append(cube, std::move(cube2));
|
||||
its_merge(cube, std::move(cube2));
|
||||
|
||||
//cube = its_make_sphere(350., 1.);
|
||||
//for (auto &face : cube2.indices)
|
||||
|
@ -732,6 +596,7 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
|
||||
// name of CGAL property map for store source object face id - index into its.indices
|
||||
std::string face_map_name = "f:face_map";
|
||||
std::string face_type_map_name = "f:type";
|
||||
// identify glyph for intersected vertex
|
||||
std::string vert_shape_map_name = "v:glyph_id";
|
||||
MyMesh cgal_object = MeshBoolean::cgal2::to_cgal(cube, face_map_name);
|
||||
|
@ -742,20 +607,17 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
std::string face_shape_map_name = "f:glyph_id";
|
||||
std::vector<ShapesVertexId> glyph_contours;
|
||||
|
||||
//std::vector<MyMesh> cgalShapes;
|
||||
//cgalShapes.reserve(shape.size());
|
||||
//for (const ExPolygon &expoly : shape) {
|
||||
// size_t index = &expoly - &shape.front();
|
||||
// cgalShapes
|
||||
//}
|
||||
|
||||
MyMesh cgal_shape = MeshBoolean::cgal2::to_cgal(shape[0], projection, 0, edge_shape_map_name, face_shape_map_name, glyph_contours);
|
||||
MyMesh cgal_shape = MeshBoolean::cgal2::to_cgal(shape, projection, 0, edge_shape_map_name, face_shape_map_name, glyph_contours);
|
||||
|
||||
auto& edge_shape_map = cgal_shape.property_map<MyMesh::Edge_index, IntersectingElemnt>(edge_shape_map_name).first;
|
||||
auto& face_shape_map = cgal_shape.property_map<MyMesh::Face_index, IntersectingElemnt>(face_shape_map_name).first;
|
||||
|
||||
//MeshBoolean::cgal2::glyph2model(shape, 0, projection, cgal_shape, glyph_contours, edge_glyph_map, face_glyph_map);
|
||||
|
||||
// bool map for affected edge
|
||||
using d_prop_bool = CGAL::dynamic_edge_property_t<bool>;
|
||||
using ecm_it = boost::property_map<MyMesh, d_prop_bool>::SMPM;
|
||||
using EcmType = CGAL::internal::Dynamic<MyMesh, ecm_it>;
|
||||
EcmType ecm = get(d_prop_bool(), cgal_object);
|
||||
|
||||
struct Visitor {
|
||||
const MyMesh &object;
|
||||
const MyMesh &shape;
|
||||
|
@ -771,15 +633,17 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||
|
||||
int32_t source_face_id;
|
||||
|
||||
int32_t source_face_id = -1;
|
||||
void before_subface_creations(face_descriptor f_old, MyMesh& mesh)
|
||||
{
|
||||
assert(&mesh == &object);
|
||||
source_face_id = face_map[f_old];
|
||||
}
|
||||
void after_subface_created(face_descriptor f_new, MyMesh& mesh) {
|
||||
// it is called multiple times for one source_face_id
|
||||
void after_subface_created(face_descriptor f_new, MyMesh &mesh)
|
||||
{
|
||||
assert(&mesh == &object);
|
||||
assert(source_face_id != -1);
|
||||
face_map[f_new] = source_face_id;
|
||||
}
|
||||
|
||||
|
@ -859,11 +723,9 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
void edge_split(halfedge_descriptor /* hnew */, MyMesh& /* tm */) {}
|
||||
void after_edge_split() {}
|
||||
void add_retriangulation_edge(halfedge_descriptor /* h */, MyMesh& /* tm */) {}
|
||||
}
|
||||
visitor { cgal_object, cgal_shape, edge_shape_map, face_shape_map, face_map, vert_shape_map};
|
||||
} visitor{cgal_object, cgal_shape, edge_shape_map, face_shape_map,
|
||||
face_map, vert_shape_map};
|
||||
|
||||
// bool map for affected edge
|
||||
auto ecm = get(CGAL::dynamic_edge_property_t<bool>(), cgal_object);
|
||||
const auto& p = CGAL::Polygon_mesh_processing::parameters::throw_on_self_intersection(false).visitor(visitor).edge_is_constrained_map(ecm);
|
||||
const auto& q = CGAL::Polygon_mesh_processing::parameters::do_not_modify(true);
|
||||
// CGAL::Polygon_mesh_processing::corefine(cgal_object, cgalcube2, p, p);
|
||||
|
@ -871,13 +733,14 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
CGAL::Polygon_mesh_processing::corefine(cgal_object, cgal_shape, p, q);
|
||||
|
||||
enum class SideType {
|
||||
// face inside of the cutted shape
|
||||
inside,
|
||||
// face outside of the cutted shape
|
||||
outside,
|
||||
// face without constrained edge (In or Out)
|
||||
not_constrained
|
||||
};
|
||||
|
||||
auto side_type_map = cgal_object.add_property_map<MyMesh::Face_index, SideType>("f:side").first;
|
||||
|
||||
for (auto fi : cgal_object.faces()) {
|
||||
SideType side_type = SideType::not_constrained;
|
||||
auto hi_end = cgal_object.halfedge(fi);
|
||||
|
@ -889,7 +752,8 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
// This face has a constrained edge.
|
||||
IntersectingElemnt shape_from = vert_shape_map[cgal_object.source(hi)];
|
||||
IntersectingElemnt shape_to = vert_shape_map[cgal_object.target(hi)];
|
||||
assert(shape_from.vertex_index != -1 && shape_from.vertex_index == shape_to.vertex_index);
|
||||
assert(shape_from.vertex_index != -1);
|
||||
assert(shape_from.vertex_index == shape_to.vertex_index);
|
||||
assert(shape_from.point_index != -1);
|
||||
assert(shape_to.point_index != -1);
|
||||
|
||||
|
@ -903,8 +767,28 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
int32_t i_from = shape_from.point_index;
|
||||
int32_t i_to = shape_to.point_index;
|
||||
if (i_from == i_to && shape_from.type == shape_to.type) {
|
||||
// Outside is detect by face orientation
|
||||
is_inside = true;
|
||||
|
||||
const auto &p = cgal_object.point(cgal_object.target(cgal_object.next(hi)));
|
||||
|
||||
int i = i_from * 2;
|
||||
int j = (i_from + 1 == int(contour.size())) ? 0 : i + 2;
|
||||
|
||||
i += vertex_index.vertex_base;
|
||||
j += vertex_index.vertex_base;
|
||||
|
||||
auto abcp =
|
||||
shape_from.type == IntersectingElemnt::Type::face_1 ?
|
||||
CGAL::orientation(
|
||||
cgal_shape.point(CGAL::SM_Vertex_index(i)),
|
||||
cgal_shape.point(CGAL::SM_Vertex_index(i + 1)),
|
||||
cgal_shape.point(CGAL::SM_Vertex_index(j)), p) :
|
||||
// shape_from.type == IntersectingElemnt::Type::face_2
|
||||
CGAL::orientation(
|
||||
cgal_shape.point(CGAL::SM_Vertex_index(j)),
|
||||
cgal_shape.point(CGAL::SM_Vertex_index(i + 1)),
|
||||
cgal_shape.point(CGAL::SM_Vertex_index(j + 1)),
|
||||
p);
|
||||
is_inside = abcp == CGAL::POSITIVE;
|
||||
} else if (i_from < i_to || (i_from == i_to && shape_from.type < shape_to.type)) {
|
||||
bool is_last = i_from == 0 && (i_to + 1) == contour.size();
|
||||
if (!is_last) is_inside = true;
|
||||
|
@ -933,12 +817,11 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
// next half edge index inside of face
|
||||
hi = cgal_object.next(hi);
|
||||
} while (hi != hi_end);
|
||||
|
||||
side_type_map[fi] = side_type;
|
||||
}
|
||||
|
||||
// debug output
|
||||
auto face_colors = cgal_object.add_property_map<MyMesh::Face_index, CGAL::Color>("f:color").first;
|
||||
auto face_colors = cgal_object.add_property_map<MyMesh::Face_index, CGAL::Color>("f:color").first;
|
||||
for (auto fi : cgal_object.faces()) {
|
||||
auto &color = face_colors[fi];
|
||||
switch (side_type_map[fi]) {
|
||||
|
@ -948,10 +831,7 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
}
|
||||
}
|
||||
CGAL::IO::write_OFF("c:\\data\\temp\\constrained.off", cgal_object);
|
||||
|
||||
|
||||
// separate by direction of extrusion
|
||||
auto vertex_colors = cgal_object.add_property_map<MyMesh::Vertex_index, CGAL::Color>("v:color").first;
|
||||
// Seed fill the other faces inside the region.
|
||||
for (Visitor::face_descriptor fi : cgal_object.faces()) {
|
||||
if (side_type_map[fi] != SideType::not_constrained) continue;
|
||||
|
@ -974,8 +854,7 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
} while (hi != hi_end);
|
||||
if (!has_inside_neighbor) continue;
|
||||
side_type_map[fi] = SideType::inside;
|
||||
|
||||
do {
|
||||
while (!queue.empty()) {
|
||||
Visitor::face_descriptor fi = queue.back();
|
||||
queue.pop_back();
|
||||
// Do not fill twice
|
||||
|
@ -992,7 +871,7 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
queue.emplace_back(fi_opposite);
|
||||
hi = cgal_object.next(hi);
|
||||
} while (hi != hi_end);
|
||||
} while (!queue.empty());
|
||||
}
|
||||
}
|
||||
|
||||
// debug output
|
||||
|
@ -1018,14 +897,14 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
std::vector<FaceState> face_states(cube.indices.size(), FaceState::Unknown);
|
||||
for (auto fi_seed : cgal_object.faces()) {
|
||||
FaceState &state = face_states[face_map[fi_seed]];
|
||||
bool m = side_type_map[fi_seed] == SideType::inside;
|
||||
bool is_face_inside = side_type_map[fi_seed] == SideType::inside;
|
||||
switch (state) {
|
||||
case FaceState::Unknown:
|
||||
state = m ? FaceState::Marked : FaceState::Unmarked;
|
||||
state = is_face_inside ? FaceState::Marked : FaceState::Unmarked;
|
||||
break;
|
||||
case FaceState::Unmarked:
|
||||
case FaceState::UnmarkedSplit:
|
||||
state = m ? FaceState::MarkedSplit : FaceState::UnmarkedSplit;
|
||||
state = is_face_inside ? FaceState::MarkedSplit : FaceState::UnmarkedSplit;
|
||||
break;
|
||||
case FaceState::Marked:
|
||||
case FaceState::MarkedSplit:
|
||||
|
@ -1048,9 +927,10 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
const FaceState state = face_states[source_face_id];
|
||||
assert(state == FaceState::Unmarked || state == FaceState::UnmarkedSplit || state == FaceState::UnmarkedEmitted ||
|
||||
state == FaceState::Marked || state == FaceState::MarkedSplit);
|
||||
if (state == FaceState::UnmarkedEmitted) {
|
||||
// Already emitted.
|
||||
} else if (state == FaceState::Unmarked || state == FaceState::UnmarkedSplit) {
|
||||
if (state == FaceState::UnmarkedEmitted) continue; // Already emitted.
|
||||
|
||||
if (state == FaceState::Unmarked ||
|
||||
state == FaceState::UnmarkedSplit) {
|
||||
// Just copy the unsplit source face.
|
||||
const Vec3i source_vertices = cube.indices[source_face_id];
|
||||
Vec3i target_vertices;
|
||||
|
@ -1063,63 +943,80 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
|||
}
|
||||
its_extruded.indices.emplace_back(target_vertices);
|
||||
face_states[source_face_id] = FaceState::UnmarkedEmitted;
|
||||
} else {
|
||||
auto hi = cgal_object.halfedge(fi);
|
||||
auto hi_prev = cgal_object.prev(hi);
|
||||
auto hi_next = cgal_object.next(hi);
|
||||
const Vec3i source_vertices{ int((std::size_t)cgal_object.target(hi)), int((std::size_t)cgal_object.target(hi_next)), int((std::size_t)cgal_object.target(hi_prev)) };
|
||||
Vec3i target_vertices;
|
||||
if (side_type_map[fi] == SideType::inside) {
|
||||
// Extrude the face. Neighbor edges separating extruded face from non-extruded face will be extruded.
|
||||
bool boundary_vertex[3] = { false, false, false };
|
||||
Vec3i target_vertices_extruded { -1, -1, -1 };
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (side_type_map[cgal_object.face(cgal_object.opposite(hi))] != SideType::inside)
|
||||
// Edge separating extruded / non-extruded region.
|
||||
boundary_vertex[i] = boundary_vertex[(i + 2) % 3] = true;
|
||||
hi = cgal_object.next(hi);
|
||||
continue; // revert modification
|
||||
}
|
||||
|
||||
auto hi = cgal_object.halfedge(fi);
|
||||
auto hi_prev = cgal_object.prev(hi);
|
||||
auto hi_next = cgal_object.next(hi);
|
||||
const Vec3i source_vertices{
|
||||
int((std::size_t)cgal_object.target(hi)),
|
||||
int((std::size_t)cgal_object.target(hi_next)),
|
||||
int((std::size_t)cgal_object.target(hi_prev)) };
|
||||
Vec3i target_vertices;
|
||||
if (side_type_map[fi] != SideType::inside) {
|
||||
// Copy the face.
|
||||
Vec3i target_vertices;
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
target_vertices(i) = map_vertices[source_vertices(i)].first;
|
||||
if (target_vertices(i) == -1) {
|
||||
map_vertices[source_vertices(i)].first = target_vertices(i) = int(its_extruded.vertices.size());
|
||||
const auto &p = cgal_object.point(cgal_object.target(hi));
|
||||
its_extruded.vertices.emplace_back(p.x(), p.y(), p.z());
|
||||
}
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
target_vertices_extruded(i) = map_vertices[source_vertices(i)].second;
|
||||
if (target_vertices_extruded(i) == -1) {
|
||||
map_vertices[source_vertices(i)].second = target_vertices_extruded(i) = int(its_extruded.vertices.size());
|
||||
const auto& p = cgal_object.point(cgal_object.target(hi));
|
||||
its_extruded.vertices.emplace_back(Vec3f{ float(p.x()), float(p.y()), float(p.z()) } + extrude_dir);
|
||||
}
|
||||
if (boundary_vertex[i]) {
|
||||
target_vertices(i) = map_vertices[source_vertices(i)].first;
|
||||
if (target_vertices(i) == -1) {
|
||||
map_vertices[source_vertices(i)].first = target_vertices(i) = int(its_extruded.vertices.size());
|
||||
const auto& p = cgal_object.point(cgal_object.target(hi));
|
||||
its_extruded.vertices.emplace_back(p.x(), p.y(), p.z());
|
||||
}
|
||||
}
|
||||
hi = cgal_object.next(hi);
|
||||
hi = cgal_object.next(hi);
|
||||
}
|
||||
its_extruded.indices.emplace_back(target_vertices);
|
||||
continue; // copy splitted triangle
|
||||
}
|
||||
|
||||
// Extrude the face. Neighbor edges separating extruded face from
|
||||
// non-extruded face will be extruded.
|
||||
bool boundary_vertex[3] = {false, false, false};
|
||||
Vec3i target_vertices_extruded{-1, -1, -1};
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (side_type_map[cgal_object.face(cgal_object.opposite(hi))] != SideType::inside)
|
||||
// Edge separating extruded / non-extruded region.
|
||||
boundary_vertex[i] = true;
|
||||
hi = cgal_object.next(hi);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
target_vertices_extruded(i) = map_vertices[source_vertices(i)].second;
|
||||
if (target_vertices_extruded(i) == -1) {
|
||||
map_vertices[source_vertices(i)].second =
|
||||
target_vertices_extruded(i) = int(
|
||||
its_extruded.vertices.size());
|
||||
const auto &p = cgal_object.point(cgal_object.target(hi));
|
||||
its_extruded.vertices.emplace_back(
|
||||
Vec3f{float(p.x()), float(p.y()), float(p.z())} +
|
||||
extrude_dir);
|
||||
}
|
||||
if (boundary_vertex[i]) {
|
||||
target_vertices(i) = map_vertices[source_vertices(i)].first;
|
||||
if (target_vertices(i) == -1) {
|
||||
map_vertices[source_vertices(i)].first = target_vertices(
|
||||
i) = int(its_extruded.vertices.size());
|
||||
const auto &p = cgal_object.point(cgal_object.target(hi));
|
||||
its_extruded.vertices.emplace_back(p.x(), p.y(), p.z());
|
||||
}
|
||||
its_extruded.indices.emplace_back(target_vertices_extruded);
|
||||
// Add the sides.
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
int j = (i + 1) % 3;
|
||||
assert(target_vertices_extruded[i] != -1 && target_vertices_extruded[j] != -1);
|
||||
if (boundary_vertex[i] && boundary_vertex[j]) {
|
||||
assert(target_vertices[i] != -1 && target_vertices[j] != -1);
|
||||
its_extruded.indices.emplace_back(Vec3i{ target_vertices[i], target_vertices[j], target_vertices_extruded[i] });
|
||||
its_extruded.indices.emplace_back(Vec3i{ target_vertices_extruded[i], target_vertices[j], target_vertices_extruded[j] });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Copy the face.
|
||||
Vec3i target_vertices;
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
target_vertices(i) = map_vertices[source_vertices(i)].first;
|
||||
if (target_vertices(i) == -1) {
|
||||
map_vertices[source_vertices(i)].first = target_vertices(i) = int(its_extruded.vertices.size());
|
||||
const auto &p = cgal_object.point(cgal_object.target(hi));
|
||||
its_extruded.vertices.emplace_back(p.x(), p.y(), p.z());
|
||||
}
|
||||
hi = cgal_object.next(hi);
|
||||
}
|
||||
its_extruded.indices.emplace_back(target_vertices);
|
||||
}
|
||||
hi = cgal_object.next(hi);
|
||||
}
|
||||
its_extruded.indices.emplace_back(target_vertices_extruded);
|
||||
// Add the sides.
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
int j = (i + 1) % 3;
|
||||
assert(target_vertices_extruded[i] != -1 &&
|
||||
target_vertices_extruded[j] != -1);
|
||||
if (boundary_vertex[i] && boundary_vertex[j]) {
|
||||
assert(target_vertices[i] != -1 && target_vertices[j] != -1);
|
||||
its_extruded.indices.emplace_back(
|
||||
Vec3i{target_vertices[i], target_vertices[j],
|
||||
target_vertices_extruded[i]});
|
||||
its_extruded.indices.emplace_back(
|
||||
Vec3i{target_vertices_extruded[i], target_vertices[j],
|
||||
target_vertices_extruded[j]});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue