Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Isotropic remeshing: add example with custom dummy sizing #7873

Merged
merged 6 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class PMPSizingField{
public:

/// @name Types
/// These types are used for the documentation of the functions of the concept and not needed implementation wise.
/// @{

/// Vertex descriptor type
Expand All @@ -38,27 +39,29 @@ typedef unspecified_type FT;
/// @name Functions
/// @{

/// a function that returns the sizing value at `v`.
FT at(const vertex_descriptor v) const;
/// returns the sizing value at `v` (used during tangential relaxation).
FT at(const vertex_descriptor v, const PolygonMesh& pmesh) const;

/// a function controlling edge split and edge collapse,
/// returning the ratio of the current edge length and the local target edge length between
/// the points of `va` and `vb` in case the current edge is too long, and `std::nullopt` otherwise.
/// returns the ratio of the current edge squared length and the local target edge squared length between
/// the points of `va` and `vb` in case the current edge is too long, and `std::nullopt` otherwise
/// (used for triggering edge splits and preventing some edge collapses).
std::optional<FT> is_too_long(const vertex_descriptor va,
const vertex_descriptor vb) const;
const vertex_descriptor vb,
const PolygonMesh& pmesh) const;

/// a function controlling edge collapse by returning the ratio of the squared length of `h` and the
/// local target edge length if it is too short, and `std::nullopt` otherwise.
/// returns the ratio of the squared length of `h` and the
/// local target edge squared length if it is too short, and `std::nullopt` otherwise
/// (used for triggering edge collapses).
std::optional<FT> is_too_short(const halfedge_descriptor h,
const PolygonMesh& pmesh) const;

/// a function returning the location of the split point of the edge of `h`.
/// returns the position of the new vertex created when splitting the edge of `h`.
Point_3 split_placement(const halfedge_descriptor h,
const PolygonMesh& pmesh) const;

/// a function that updates the sizing field value at the vertex `v`.
void update(const vertex_descriptor v,
const PolygonMesh& pmesh);
/// function called after the addition of the split vertex `v` in `pmesh`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we mention that the sizing field at v may have to be updated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sizing field class does register it and does what it needs to do. Uniform_sizing is not using it and neither does the custom example. Something like, "can be used to updated pre-computed sizing" can be added if you want to insist on that point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I think that it is important to say it. If there is nothing to update, then it's easy. Otherwise it's good to know that it is here that the user should do it.

void register_split_vertex(const vertex_descriptor v,
const PolygonMesh& pmesh);

/// @}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ create_single_source_cgal_program("match_faces.cpp")
create_single_source_cgal_program("cc_compatible_orientations.cpp")
create_single_source_cgal_program("hausdorff_distance_remeshing_example.cpp")
create_single_source_cgal_program("hausdorff_bounded_error_distance_example.cpp")
create_single_source_cgal_program("isotropic_remeshing_with_custom_sizing_example.cpp")

find_package(Eigen3 3.2.0 QUIET) #(requires 3.2.0 or greater)
include(CGAL_Eigen3_support)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
#include <CGAL/Polygon_mesh_processing/bbox.h>

#include <fstream>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;

namespace PMP = CGAL::Polygon_mesh_processing;

// a sizing fied that is increasing the size of edge along the y-axis
// starting at a minimum size at y-max and ending at a maximum size at
// y-min, with a linear interpolation of sizes in between the two extreme
// sizing values
struct My_sizing_field
{
double min_size, max_size;
double ymin, ymax;

My_sizing_field(double min_size, double max_size, double ymin, double ymax)
: min_size(min_size)
, max_size(max_size)
, ymin(ymin)
, ymax(ymax)
{}

double at(K::Point_3 p) const
{
double y=p.y();
return CGAL::square( (y-ymin)/(ymax-ymin) * (min_size - max_size) + max_size );
}
double at(const Mesh::Vertex_index v, const Mesh& mesh) const { return at(mesh.point(v)); }

std::optional<double> is_too_long(const Mesh::Vertex_index va,
const Mesh::Vertex_index vb,
const Mesh& mesh) const
{
// TODO: no mesh as parameters?
K::Point_3 mp = CGAL::midpoint(mesh.point(va), mesh.point(vb));
double sql_at = at(mp);
double sql = CGAL::squared_distance(mesh.point(va), mesh.point(vb));
if (sql > sql_at)
return sql / sql_at;
return std::nullopt;
}

std::optional<double> is_too_short(const Mesh::Halfedge_index h,
const Mesh& mesh) const
{
K::Point_3 mp = CGAL::midpoint(mesh.point(source(h, mesh)), mesh.point(target(h, mesh)));
double sql_at = at(mp);
double sql = CGAL::squared_distance(mesh.point(source(h, mesh)), mesh.point(target(h, mesh)));
if (sql < sql_at)
return sql / sql_at;
return std::nullopt;
}

K::Point_3 split_placement(const Mesh::Halfedge_index h,
const Mesh& mesh) const
{
return CGAL::midpoint(mesh.point(source(h, mesh)), mesh.point(target(h, mesh)));
}

void register_split_vertex(const Mesh::Vertex_index, const Mesh&) {}
};


int main(int argc, char* argv[])
{
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/elk.off");

Mesh mesh;
if (!PMP::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) {
std::cerr << "Not a valid input file." << std::endl;
return 1;
}

std::cout << "Start remeshing of " << filename
<< " (" << num_faces(mesh) << " faces)..." << std::endl;

CGAL::Bbox_3 bb = PMP::bbox(mesh);
My_sizing_field sizing_field(0.1, 30, bb.ymin(), bb.ymax());
unsigned int nb_iter = 5;

PMP::isotropic_remeshing(
faces(mesh),
sizing_field,
mesh,
CGAL::parameters::number_of_iterations(nb_iter)
.number_of_relaxation_steps(3)
);

CGAL::IO::write_polygon_mesh("custom_remesh_out.off", mesh, CGAL::parameters::stream_precision(17));

std::cout << "Remeshing done." << std::endl;

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ namespace Polygon_mesh_processing
* Edges too long with respect to the local target edge length are split in two, while
* edges that are too short are collapsed.
*
* This class depends on the Eigen library.
lrineau marked this conversation as resolved.
Show resolved Hide resolved
*
* \cgalModels{PMPSizingField}
*
* \sa `isotropic_remeshing()`
Expand Down Expand Up @@ -213,13 +215,13 @@ class Adaptive_sizing_field
}

public:
FT at(const vertex_descriptor v) const
FT at(const vertex_descriptor v, const PolygonMesh& /* pmesh */) const
{
CGAL_assertion(get(m_vertex_sizing_map, v));
return get(m_vertex_sizing_map, v);
}

std::optional<FT> is_too_long(const vertex_descriptor va, const vertex_descriptor vb) const
std::optional<FT> is_too_long(const vertex_descriptor va, const vertex_descriptor vb, const PolygonMesh& /* pmesh */) const
{
const FT sqlen = sqlength(va, vb);
FT sqtarg_len = CGAL::square(4./3. * (CGAL::min)(get(m_vertex_sizing_map, va),
Expand Down Expand Up @@ -251,7 +253,7 @@ class Adaptive_sizing_field
get(m_vpmap, source(h, pmesh)));
}

void update(const vertex_descriptor v, const PolygonMesh& pmesh)
void register_split_vertex(const vertex_descriptor v, const PolygonMesh& pmesh)
{
// calculating it as the average of two vertices on other ends
// of halfedges as updating is done during an edge split
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,12 @@ class Uniform_sizing_field
}

public:
FT at(const vertex_descriptor /* v */) const
FT at(const vertex_descriptor /* v */, const PolygonMesh& /* pmesh */) const
{
return m_size;
}

std::optional<FT> is_too_long(const vertex_descriptor va, const vertex_descriptor vb) const
std::optional<FT> is_too_long(const vertex_descriptor va, const vertex_descriptor vb, const PolygonMesh& /* pmesh */) const
{
const FT sqlen = sqlength(va, vb);
if (sqlen > m_sq_long)
Expand All @@ -133,7 +133,7 @@ class Uniform_sizing_field
get(m_vpmap, source(h, pmesh)));
}

void update(const vertex_descriptor /* v */, const PolygonMesh& /* pmesh */)
void register_split_vertex(const vertex_descriptor /* v */, const PolygonMesh& /* pmesh */)
{}

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ namespace internal {
get(ecmap, e) ||
get(fpm, face(h,pmesh))!=get(fpm, face(opposite(h,pmesh),pmesh)) )
{
if (sizing.is_too_long(source(h, pmesh), target(h, pmesh)))
if (sizing.is_too_long(source(h, pmesh), target(h, pmesh), pmesh))
{
return false;
}
Expand Down Expand Up @@ -400,7 +400,7 @@ namespace internal {
for(edge_descriptor e : edge_range)
{
const halfedge_descriptor he = halfedge(e, mesh_);
std::optional<double> sqlen = sizing.is_too_long(source(he, mesh_), target(he, mesh_));
std::optional<double> sqlen = sizing.is_too_long(source(he, mesh_), target(he, mesh_), mesh_);
if(sqlen != std::nullopt)
long_edges.emplace(he, sqlen.value());
}
Expand Down Expand Up @@ -433,16 +433,16 @@ namespace internal {
std::cout << " refinement point : " << refinement_point << std::endl;
#endif
//update sizing field with the new point
sizing.update(vnew, mesh_);
sizing.register_split_vertex(vnew, mesh_);

//check sub-edges
//if it was more than twice the "long" threshold, insert them
std::optional<double> sqlen_new = sizing.is_too_long(source(hnew, mesh_), target(hnew, mesh_));
std::optional<double> sqlen_new = sizing.is_too_long(source(hnew, mesh_), target(hnew, mesh_), mesh_);
if(sqlen_new != std::nullopt)
long_edges.emplace(hnew, sqlen_new.value());

const halfedge_descriptor hnext = next(hnew, mesh_);
sqlen_new = sizing.is_too_long(source(hnext, mesh_), target(hnext, mesh_));
sqlen_new = sizing.is_too_long(source(hnext, mesh_), target(hnext, mesh_), mesh_);
if (sqlen_new != std::nullopt)
long_edges.emplace(hnext, sqlen_new.value());

Expand Down Expand Up @@ -500,7 +500,7 @@ namespace internal {
if (!is_split_allowed(e))
continue;
const halfedge_descriptor he = halfedge(e, mesh_);
std::optional<double> sqlen = sizing.is_too_long(source(he, mesh_), target(he, mesh_));
std::optional<double> sqlen = sizing.is_too_long(source(he, mesh_), target(he, mesh_), mesh_);
if(sqlen != std::nullopt)
long_edges.emplace(halfedge(e, mesh_), sqlen.value());
}
Expand Down Expand Up @@ -550,16 +550,16 @@ namespace internal {
halfedge_added(hnew_opp, status(opposite(he, mesh_)));

//update sizing field with the new point
sizing.update(vnew, mesh_);
sizing.register_split_vertex(vnew, mesh_);

//check sub-edges
//if it was more than twice the "long" threshold, insert them
std::optional<double> sqlen_new = sizing.is_too_long(source(hnew, mesh_), target(hnew, mesh_));
std::optional<double> sqlen_new = sizing.is_too_long(source(hnew, mesh_), target(hnew, mesh_), mesh_);
if(sqlen_new != std::nullopt)
long_edges.emplace(hnew, sqlen_new.value());

const halfedge_descriptor hnext = next(hnew, mesh_);
sqlen_new = sizing.is_too_long(source(hnext, mesh_), target(hnext, mesh_));
sqlen_new = sizing.is_too_long(source(hnext, mesh_), target(hnext, mesh_), mesh_);
if (sqlen_new != std::nullopt)
long_edges.emplace(hnext, sqlen_new.value());

Expand All @@ -580,7 +580,7 @@ namespace internal {

if (snew == PATCH)
{
std::optional<double> sql = sizing.is_too_long(source(hnew2, mesh_), target(hnew2, mesh_));
std::optional<double> sql = sizing.is_too_long(source(hnew2, mesh_), target(hnew2, mesh_), mesh_);
if(sql != std::nullopt)
long_edges.emplace(hnew2, sql.value());
}
Expand All @@ -603,7 +603,7 @@ namespace internal {

if (snew == PATCH)
{
std::optional<double> sql = sizing.is_too_long(source(hnew2, mesh_), target(hnew2, mesh_));
std::optional<double> sql = sizing.is_too_long(source(hnew2, mesh_), target(hnew2, mesh_), mesh_);
if (sql != std::nullopt)
long_edges.emplace(hnew2, sql.value());
}
Expand Down Expand Up @@ -747,7 +747,7 @@ namespace internal {
for(halfedge_descriptor ha : halfedges_around_target(va, mesh_))
{
vertex_descriptor va_i = source(ha, mesh_);
std::optional<double> sqha = sizing.is_too_long(vb, va_i);
std::optional<double> sqha = sizing.is_too_long(vb, va_i, mesh_);
if (sqha != std::nullopt)
{
collapse_ok = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ class Sizing_field_base
typedef typename K::FT FT;

public:
virtual FT at(const vertex_descriptor v) const = 0;
virtual FT at(const vertex_descriptor v, const PolygonMesh&) const = 0;
virtual std::optional<FT> is_too_long(const vertex_descriptor va,
const vertex_descriptor vb) const = 0;
const vertex_descriptor vb,
const PolygonMesh&) const = 0;
virtual std::optional<FT> is_too_short(const halfedge_descriptor h,
const PolygonMesh& pmesh) const = 0;
virtual Point_3 split_placement(const halfedge_descriptor h, const PolygonMesh& pmesh) const = 0;
virtual void update(const vertex_descriptor v, const PolygonMesh& pmesh) = 0;
virtual void register_split_vertex(const vertex_descriptor v, const PolygonMesh& pmesh) = 0;

};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,9 @@ void tangential_relaxation(const VertexRange& vertices,

const double tri_area = gt_area(get(vpm, v), get(vpm, v1), get(vpm, v2));
const double face_weight = tri_area
/ (1. / 3. * (sizing.at(v)
+ sizing.at(v1)
+ sizing.at(v2)));
/ (1. / 3. * (sizing.at(v, tm)
+ sizing.at(v1, tm)
+ sizing.at(v2, tm)));
weight += face_weight;

const Point_3 centroid = gt_centroid(get(vpm, v), get(vpm, v1), get(vpm, v2));
Expand Down