B1 (api-performance): wire hyper_ideal_hessian_block_fd_sym into newton_hyper_ideal instead of the full-FD path — 33×/1166× faster on cathead/brezel, also fixes the FD-step vs Newton-tol accuracy floor (N1 cross-fix). V3 (input-validation): add isfinite check on all vertex coordinates in load_mesh; NaN/Inf now throws runtime_error at the I/O boundary before poisoning the solver. C1 (test-coverage): expose the internal ok flag via an optional bool* parameter on solve_linear_system so double-solver failure is no longer invisible to callers. +5 new tests (LoadMeshThrowsOnNaN/Inf, OkFlag_True*, OkFlag_NullPointerIsSafe). 282/282 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
84 lines
2.9 KiB
C++
84 lines
2.9 KiB
C++
#pragma once
|
|
// Copyright (c) 2024-2026 Tarik Moussa.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// mesh_io.hpp
|
|
//
|
|
// Phase 4b — CGAL::IO wrappers for ConformalMesh.
|
|
//
|
|
// Reads and writes ConformalMesh (CGAL::Surface_mesh) in standard polygon-mesh
|
|
// formats using the CGAL Polygon Mesh I/O utilities (CGAL 5.4+).
|
|
//
|
|
// Supported formats (detected by file extension):
|
|
// .off — Object File Format (read + write)
|
|
// .obj — Wavefront OBJ (read + write)
|
|
// .ply — Polygon File Format (read + write)
|
|
//
|
|
// Usage:
|
|
// ConformalMesh mesh;
|
|
// if (!read_mesh("input.off", mesh)) throw std::runtime_error("read failed");
|
|
// // ... process mesh ...
|
|
// write_mesh("output.off", mesh);
|
|
//
|
|
// Note: property maps (lambda0, v_idx, etc.) are NOT serialised — they must
|
|
// be re-initialised with setup_*_maps() + compute_lambda0_from_mesh() after
|
|
// reading a file.
|
|
|
|
#include "conformal_mesh.hpp"
|
|
#include <CGAL/IO/polygon_mesh_io.h>
|
|
#include <CGAL/boost/graph/helpers.h>
|
|
#include <string>
|
|
#include <stdexcept>
|
|
#include <cmath>
|
|
|
|
namespace conformallab {
|
|
|
|
/// Read a polygon mesh from `filename` into `mesh` (clears existing content).
|
|
/// Returns `true` on success, `false` on failure.
|
|
inline bool read_mesh(const std::string& filename, ConformalMesh& mesh)
|
|
{
|
|
mesh.clear();
|
|
return CGAL::IO::read_polygon_mesh(filename, mesh);
|
|
}
|
|
|
|
/// Write `mesh` to `filename`. Returns `true` on success.
|
|
inline bool write_mesh(const std::string& filename, const ConformalMesh& mesh)
|
|
{
|
|
return CGAL::IO::write_polygon_mesh(filename, mesh);
|
|
}
|
|
|
|
/// Throwing wrapper around `read_mesh`: returns the mesh by value
|
|
/// or throws `std::runtime_error` on read failure.
|
|
///
|
|
/// Also enforces that the mesh is triangulated, because every functional
|
|
/// in this library assumes triangle faces — a quad/polygon mesh would be
|
|
/// read in silently and then mis-handled by the angle/length formulas.
|
|
/// Fail loudly here at the I/O boundary instead.
|
|
inline ConformalMesh load_mesh(const std::string& filename)
|
|
{
|
|
ConformalMesh mesh;
|
|
if (!read_mesh(filename, mesh))
|
|
throw std::runtime_error("conformallab: failed to read mesh from " + filename);
|
|
if (!CGAL::is_triangle_mesh(mesh))
|
|
throw std::runtime_error(
|
|
"conformallab: mesh from " + filename +
|
|
" is not triangulated (functionals require triangle faces)");
|
|
for (auto v : mesh.vertices()) {
|
|
const auto& p = mesh.point(v);
|
|
if (!std::isfinite(p.x()) || !std::isfinite(p.y()) || !std::isfinite(p.z()))
|
|
throw std::runtime_error(
|
|
"conformallab: non-finite vertex coordinate in " + filename);
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
/// Throwing wrapper around `write_mesh`; throws `std::runtime_error` on
|
|
/// write failure.
|
|
inline void save_mesh(const std::string& filename, const ConformalMesh& mesh)
|
|
{
|
|
if (!write_mesh(filename, mesh))
|
|
throw std::runtime_error("conformallab: failed to write mesh to " + filename);
|
|
}
|
|
|
|
} // namespace conformallab
|