Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/patr-schm/surface-maps-via-adaptive-triangulations
30 May 2023, 12:10:02 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    No releases to show
  • 3367d82
  • /
  • src
  • /
  • SurfaceMaps
  • /
  • Utils
  • /
  • IO.cc
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:4c0b6c9bc78d7a8565d3509669b50f0664a02251
origin badgedirectory badge
swh:1:dir:c2f78cca852cb7cbbf962da63e033e1992b1c0f2
origin badgerevision badge
swh:1:rev:da768497f0b8a84088b7f0d839b8b40a1780c672
origin badgesnapshot badge
swh:1:snp:b226c256e24f85cfb68346d404868c5d0bb72cfd

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: da768497f0b8a84088b7f0d839b8b40a1780c672 authored by Patrick Schmidt on 11 May 2023, 16:25:14 UTC
Add grsi-thumbnail.png
Tip revision: da76849
IO.cc
/*
 * This file is part of
 * Surface Maps via Adaptive Triangulations
 * (https://github.com/patr-schm/surface-maps-via-adaptive-triangulations)
 * and is released under the MIT license.
 *
 * Authors: Patrick Schmidt, Janis Born
 */

#include "IO.hh"

#include <polymesh/Mesh.hh>
#include <glow/objects/Texture2D.hh>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <SurfaceMaps/Utils/Helpers.hh>
#include <SurfaceMaps/Utils/BarycentricPoint.hh>

#include <string>
#include <fstream>

namespace SurfaceMaps
{

/// Create directory (recursive)
void make_directory(
        const std::string& _dir_path)
{
    fs::create_directories(_dir_path);
}

void make_file_directory(
        const std::string& _file_path)
{
    fs::create_directories(fs::path(_file_path).parent_path());
}

std::ifstream open_file_read(
        const std::string& _path,
        const bool _throw_if_not_exist)
{
    std::ifstream file(_path);
    if (!file.good() && _throw_if_not_exist)
        ISM_ERROR_throw("Could not open file " << _path);

    return file;
}

void remove_comment(
        std::string& str,
        std::string comment)
{
    str = str.substr(0, str.find_first_of(comment));
}

void trim(
        std::string& str)
{
    str.erase(0, str.find_first_not_of('\r'));
    str.erase(str.find_last_not_of('\r') + 1 );
    str.erase(0, str.find_first_not_of('\n'));
    str.erase(str.find_last_not_of('\n') + 1 );
    str.erase(0, str.find_first_not_of(' '));
    str.erase(str.find_last_not_of(' ') + 1 );
}

std::vector<std::string> read_line(
        std::ifstream& _file,
        const char _delimiter)
{
    std::vector<std::string> tokens;

    std::string line_string;
    if (!std::getline(_file, line_string))
        return tokens;

    // Remove comments
    remove_comment(line_string, "#");
    trim(line_string);

    if (line_string.empty())
        return tokens;

    // Split string into tokens
    std::istringstream line_stream(line_string);
    std::string token;
    while (std::getline(line_stream, token, _delimiter))
    {
        trim(token);
        if (token.empty())
            continue;

        tokens.push_back(token);
    }

    return tokens;
}

void append_line(
        const fs::path& _file_path,
        const std::string& _line,
        const bool _silent)
{
    std::ofstream file(_file_path, std::ofstream::app);
    ISM_ASSERT(file.good());

    file << _line << std::endl;

    if (!_silent)
        ISM_INFO("Wrote (append) " << _file_path);
}

namespace
{

const auto max_double_precision = std::numeric_limits<long double>::max_digits10 + 2;

bool file_exists(
        const std::string& _file_path)
{
    auto p = fs::path(_file_path);
    return fs::exists(p) && !fs::is_directory(p);
}

bool obj_has_texture_coordinates(
        const fs::path& _path)
{
    ISM_ASSERT_EQ(_path.extension(), ".obj");
    auto file = open_file_read(_path, true);

    std::string line;
    while (std::getline(file, line))
    {
        if (line.find("vt") != std::string::npos)
            return true;
    }

    return false;
}

}

TriMesh read_mesh(
        const fs::path& _path,
        const bool _silent)
{
    if (!file_exists(_path))
        ISM_ERROR_throw("Could not open file " << _path);

    TriMesh mesh;

    // Detect whether the mesh comes with texture coordinates before calling request_...
    // Is this really necessary?
    if (_path.extension() == ".om")
    {
        OpenMesh::IO::Options options;
        options += OpenMesh::IO::Options::FaceTexCoord;
        ISM_ASSERT(OpenMesh::IO::read_mesh(mesh, _path, options));
    }
    else if (_path.extension() == ".obj" && obj_has_texture_coordinates(_path))
    {
        mesh.request_halfedge_texcoords2D();
        OpenMesh::IO::Options options;
        options += OpenMesh::IO::Options::FaceTexCoord;
        ISM_ASSERT(OpenMesh::IO::read_mesh(mesh, _path, options));
    }
    else
    {
        ISM_ASSERT(OpenMesh::IO::read_mesh(mesh, _path));
    }

    if (!_silent)
        ISM_INFO("Read " << _path);

    return mesh;
}

void write_mesh(
        const TriMesh& _mesh,
        const fs::path& _file_path)
{
    make_file_directory(_file_path);

    OpenMesh::IO::Options options;
    if (_mesh.has_vertex_texcoords2D())
        options += OpenMesh::IO::Options::VertexTexCoord;
    if (_mesh.has_halfedge_texcoords2D())
        options += OpenMesh::IO::Options::FaceTexCoord;
    if (_mesh.has_vertex_normals())
        options += OpenMesh::IO::Options::VertexNormal;

    const bool success = OpenMesh::IO::write_mesh(_mesh, _file_path, options, max_double_precision);
    if (success)
        ISM_INFO("Wrote " << _file_path)
    else
        ISM_ERROR_throw("Failed to write " << _file_path);
}

TexCoords tex_coords(
        const TriMesh& _mesh)
{
    ISM_ASSERT(_mesh.has_halfedge_texcoords2D());
    return TexCoords(_mesh, _mesh.halfedges().to_vector([&] (auto h)
    {
        return _mesh.texcoord2D(h);
    }));
}

namespace
{

template <typename T>
std::vector<T> read_element_handles(
        const std::string& _path,
        const bool _throw_if_not_exist)
{
    std::vector<T> handles;
    auto file = open_file_read(_path, _throw_if_not_exist);

    if (!file.good())
        return handles;

    std::string line;
    while (std::getline(file, line))
    {
        // Cut off comments and trim
        remove_comment(line, "#");
        trim(line);

        if (line.empty())
            continue;

        handles.push_back(T(std::stoi(line)));
    }

    ISM_INFO("Read " << _path);

    return handles;
}

template <typename T>
void write_element_handles(
        const std::vector<T>& _handles,
        const std::string& _path,
        const bool _overwrite)
{
    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }

    std::ofstream file(_path, std::ofstream::trunc);
    for (const auto& h : _handles)
        file << std::to_string(h.idx()) << std::endl;

    ISM_INFO("Wrote " << _path);
}

template <typename OM_Handle, typename PM_Handle>
std::vector<OM_Handle> openmesh_handles(
        const std::vector<PM_Handle>& _pm)
{
    std::vector<OM_Handle> om;
    for (const auto& h : _pm)
        om.push_back(OM_Handle(h.idx.value));

    return om;
}

template <typename PM_Handle, typename OM_Handle>
std::vector<PM_Handle> polymesh_handles(
        const std::vector<OM_Handle>& _om,
        const pm::Mesh& _mesh)
{
    std::vector<PM_Handle> pm;
    for (const auto& h : _om)
        pm.push_back(_mesh.handle_of(typename PM_Handle::index_t(h.idx())));

    return pm;
}

}

LandmarkFormat guess_landmark_format(
        const fs::path& _path)
{
    std::string ext = _path.extension();
    for (char& c : ext)
        c = std::tolower(c);

    if (ext == ".pinned")
        return LandmarkFormat::Pinned;
    else if (ext == ".vts")
        return LandmarkFormat::VTS;
    else if (ext == ".txt")
        return LandmarkFormat::Pinned;
    else
        return LandmarkFormat::Unknown;
}

std::vector<VH> read_landmarks(
        const fs::path& _path,
        const LandmarkType _type,
        const bool _throw_if_not_exist)
{
    const LandmarkFormat format = guess_landmark_format(_path);
    if (format == LandmarkFormat::Pinned)
        return read_landmarks_pinned(_path, _type, _throw_if_not_exist);
    else if (format == LandmarkFormat::VTS)
        return read_landmarks_vts(_path, _throw_if_not_exist);
    else
        ISM_ERROR_throw("Unrecognized landmark format");
}

std::vector<VH> read_landmarks_pinned(
        const fs::path& _path,
        const LandmarkType _type,
        const bool _throw_if_not_exist)
{
    std::vector<VH> landmarks;
    auto file = open_file_read(_path, _throw_if_not_exist);

    if (!file.good())
        return landmarks;

    while (!file.eof())
    {
        const auto line = read_line(file, ' ');
        if (line.empty())
            continue;

        if (line.size() == 1)
            landmarks.push_back(VH(std::stoi(line[0])));
        else if (line.size() == 2)
        {
            if (line[0] == "li")
            {
                if (_type == LandmarkType::Init)
                    landmarks.push_back(VH(std::stoi(line[1])));
            }
            else if (line[0] == "lp")
            {
                if (_type == LandmarkType::Init ||
                    _type == LandmarkType::PreOpt)
                    landmarks.push_back(VH(std::stoi(line[1])));
            }
            else if (line[0] == "lk")
            {
                landmarks.push_back(VH(std::stoi(line[1])));
            }
            else
                ISM_ERROR_throw("Unsupported landmark format");
        }
        else
            ISM_ERROR_throw("Unsupported landmark format");
    }

    ISM_INFO("Read " << _path);

    return landmarks;
}

std::vector<VH> read_landmarks_vts(
        const fs::path& _path,
        const bool _throw_if_not_exist)
{
    auto f = open_file_read(_path, _throw_if_not_exist);
    std::vector<VH> result;
    while (f.good())
    {
        int id;
        float x, y, z; // Unused
        f >> id >> x >> y >> z;
        if (f.good())
            result.push_back(VH(id));
    }
    return result;
}

void write_landmarks(
        const std::vector<VH>& _landmarks,
        const fs::path& _path,
        const std::vector<LandmarkType>& _landmark_types,
        const bool _overwrite)
{
    const LandmarkFormat format = guess_landmark_format(_path);
    if (format == LandmarkFormat::Pinned)
        write_landmarks_pinned(_landmarks, _path, _landmark_types, _overwrite);
    else if (format == LandmarkFormat::VTS)
        write_landmarks_vts(_landmarks, _path, _overwrite);
    else
        ISM_ERROR_throw("Unrecognized landmark format");
}

void write_landmarks_pinned(
        const std::vector<VH>& _landmarks,
        const fs::path& _path,
        const std::vector<LandmarkType>& _landmark_types,
        const bool _overwrite)
{
    ISM_ASSERT(_landmarks.size() == _landmark_types.size() || _landmark_types.empty());

    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }

    std::ofstream file(_path, std::ofstream::trunc);
    for (int i = 0; i < _landmarks.size(); ++i)
    {
        if (_landmark_types.size() == _landmarks.size())
        {
            if (_landmark_types[i] == LandmarkType::Init)
                file << "li ";
            else if (_landmark_types[i] == LandmarkType::PreOpt)
                file << "lp ";
            else if (_landmark_types[i] == LandmarkType::Keep)
                file << "lk ";
            else
                ISM_ERROR_throw("Landmark type not supported.");
        }
        file << std::to_string(_landmarks[i].idx()) << std::endl;
    }

    ISM_INFO("Wrote " << _path);
}

void write_landmarks_vts(
        const std::vector<VH>& _landmarks,
        const fs::path& _path,
        const bool _overwrite)
{
    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }
    std::ofstream f(_path);
    for (const auto& vh : _landmarks)
    {
        f << vh.idx(); // ID
        f << " 0.0 0.0 0.0\n"; // Dummy position
    }
}

std::vector<EH> read_paths(
        const std::string& _path,
        const bool _throw_if_not_exist)
{
    return read_element_handles<EH>(_path, _throw_if_not_exist);
}

std::vector<pm::edge_handle> read_paths(
        const std::string& _path,
        const pm::Mesh& _mesh,
        const bool _throw_if_not_exist)
{
    return polymesh_handles<pm::edge_handle>(read_paths(_path, _throw_if_not_exist), _mesh);
}

void write_paths(
        const std::vector<EH>& _edges,
        const std::string& _path,
        const bool _overwrite)
{
    write_element_handles(_edges, _path, _overwrite);
}

void write_paths(
        const std::vector<pm::edge_handle>& _edges,
        const std::string& _path,
        const bool _overwrite)
{
    write_element_handles(openmesh_handles<EH>(_edges), _path, _overwrite);
}

glow::SharedTexture2D read_texture(
        const fs::path &_file_path)
{
    return glow::Texture2D::createFromFile(_file_path, glow::ColorSpace::sRGB);
}

ExternalProperty<VH, Vec3d> embedding_from_mesh(
        const TriMesh& _mesh)
{
    ExternalProperty<VH, Vec3d> embedding(_mesh);
    for (auto v : _mesh.vertices())
        embedding[v] = _mesh.point(v);

    return embedding;
}

ExternalProperty<VH, Vec3d> read_embedding(
        const TriMesh& _mesh,
        const fs::path& _path)
{
    const TriMesh embedded_mesh = read_mesh(_path);
    ISM_ASSERT_EQ(embedded_mesh.n_vertices(), _mesh.n_vertices());

    ExternalProperty<VH, Vec3d> embedding(_mesh);
    for (auto v : _mesh.vertices())
        embedding[v] = embedded_mesh.point(v);

    return embedding;
}

void write_embedding(
        const TriMesh& _mesh,
        const ExternalProperty<VH, Vec3d>& _embedding,
        const fs::path& _path)
{
    ISM_ASSERT(_embedding.size_okay(_mesh));

    TriMesh embedded_mesh = _mesh; // copy
    for (auto v : _mesh.vertices())
        embedded_mesh.point(v) = _embedding[v];

    write_mesh(embedded_mesh, _path);
}

void mesh_to_matrix(
        const TriMesh& _mesh,
        MatXd& _V, MatXi& _F)
{
    _V.resize(_mesh.n_vertices(), 3);
    for (const auto vh : _mesh.vertices())
    {
        const auto& p = _mesh.point(vh);
        for (int col = 0; col < 3; ++col)
            _V(vh.idx(), col) = p[col];
    }

    _F.resize(_mesh.n_faces(), 3);
    for (const auto fh : _mesh.faces())
    {
        const auto heh = _mesh.halfedge_handle(fh);
        _F(fh.idx(), 0) = _mesh.to_vertex_handle(heh).idx();
        _F(fh.idx(), 1) = _mesh.to_vertex_handle(_mesh.next_halfedge_handle(heh)).idx();
        _F(fh.idx(), 2) = _mesh.from_vertex_handle(heh).idx();
    }
}

void matrix_to_mesh(
        const MatXd& _V,
        const MatXi& _F,
        TriMesh& _mesh,
        const bool _compute_normals/* = false*/)
{
    _mesh.clear();
    _mesh.reserve(_V.rows(), _V.rows() + _F.rows(), _F.rows());

    for (int i = 0; i < _V.rows(); ++i)
        _mesh.add_vertex(Vec3d(_V(i, 0), _V(i, 1), _V(i, 2)));
    for (int i = 0; i < _F.rows(); ++i)
        _mesh.add_face(VH(_F(i, 0)), VH(_F(i, 1)), VH(_F(i, 2)));

    if (_compute_normals)
    {
        _mesh.request_face_normals();
        _mesh.update_face_normals();
        _mesh.request_vertex_normals();
        _mesh.update_vertex_normals();
    }
}

MatXd param_to_matrix(
        const Parametrization& _param,
        const TriMesh& _mesh)
{
    MatXd M(_mesh.n_vertices(), 2);
    for (auto vh : _mesh.vertices())
    {
        M(vh.idx(), 0) = _param[vh][0];
        M(vh.idx(), 1) = _param[vh][1];
    }

    return M;
}

/// Convert libigl matrix to parameterization
Parametrization param_from_matrix(
        const MatXd& _P,
        const TriMesh& _mesh)
{
    ISM_ASSERT_EQ(_P.rows(), (int)_mesh.n_vertices());
    ISM_ASSERT_EQ(_P.cols(), 2);

    Parametrization param(_mesh);
    for (auto vh : _mesh.vertices())
        param[vh] = Vec2d(_P(vh.idx(), 0), _P(vh.idx(), 1));

    return param;
}

pm::vertex_attribute<Vec3d> to_polymesh(
        const TriMesh& mesh,
        pm::Mesh& m)
{
    for (auto vh : mesh.vertices())
        m.vertices().add();
    for (auto fh : mesh.faces())
    {
        VH vh_a, vh_b, vh_c;
        handles(mesh, fh, vh_a, vh_b, vh_c);
        m.faces().add(m.handle_of(pm::vertex_index(vh_b.idx())),
                      m.handle_of(pm::vertex_index(vh_c.idx())),
                      m.handle_of(pm::vertex_index(vh_a.idx())));
    }

    auto pos = m.vertices().make_attribute<Vec3d>();
    for (auto vh : mesh.vertices())
        pos[m.handle_of(pm::vertex_index(vh.idx()))] = mesh.point(vh);

    return pos;
}

pm::vertex_attribute<tg::pos3> to_polymesh_tg(
        const TriMesh& mesh,
        pm::Mesh& m)
{
    for (auto vh : mesh.vertices())
        m.vertices().add();
    for (auto fh : mesh.faces())
    {
        VH vh_a, vh_b, vh_c;
        handles(mesh, fh, vh_a, vh_b, vh_c);
        m.faces().add(m.handle_of(pm::vertex_index(vh_b.idx())),
                      m.handle_of(pm::vertex_index(vh_c.idx())),
                      m.handle_of(pm::vertex_index(vh_a.idx())));
    }

    auto pos = m.vertices().make_attribute<tg::pos3>();
    for (auto vh : mesh.vertices())
        pos[m.handle_of(pm::vertex_index(vh.idx()))] = tg::pos3(mesh.point(vh));

    return pos;
}

MatXd uv_vector_to_matrix(
        const VecXd& _uv_vec)
{
    MatXd uv_mat(_uv_vec.rows() / 2, 2);

    for (uint i = 0; i < uv_mat.rows(); ++i)
    {
        uv_mat(i, 0) = _uv_vec[2 * i];
        uv_mat(i, 1) = _uv_vec[2 * i + 1];
    }

    return uv_mat;
}

Parametrization vector_to_param(
        const VecXd& _x,
        const TriMesh _mesh)
{
    Parametrization param(_mesh);

    for (uint i = 0; i < _mesh.n_vertices(); ++i)
    {
        const VH vh(i);
        param[vh] = Vec2d(_x[2 * i], _x[2 * i + 1]);
    }

    return param;
}

VecXd matrix_to_stacked_vector(
        const MatXd &_mat)
{
    VecXd x;
    x.resize(2 * _mat.rows());

    for (uint i = 0; i < _mat.rows(); ++i)
    {
        x[i] = _mat(i, 0);
        x[i + _mat.rows()] = _mat(i, 1);
    }
    return x;
}

/// Read double per mesh element
ExternalProperty<VH, double> read_per_vertex_data(
        const std::string& _path,
        const TriMesh& _mesh,
        const bool _throw_if_not_exist)
{
    return read_property<VH>(_path, _mesh, _throw_if_not_exist);
}

/// Write double-valued vertex property
void write_vertex_property(
        const OpenMesh::VPropHandleT<double> _ph,
        const std::string& _path,
        const TriMesh& _mesh,
        const bool _overwrite)
{
    make_file_directory(_path);

    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }

    std::ofstream file(_path);
    ISM_ASSERT(file.is_open());
    for (auto vh : _mesh.vertices())
        file << std::to_string(_mesh.property(_ph, vh)) << std::endl;

    ISM_INFO("Wrote " << _path);
}

void write_vertex_to_point_map(
        const VertexToPointMap& _vtpm,
        const std::string& _path,
        const TriMesh& _source,
        const TriMesh& _target,
        const bool _overwrite)
{
    ISM_ASSERT(_vtpm.size_okay(_source));

    make_file_directory(_path);
    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }

    std::ofstream file(_path);
    ISM_ASSERT(file.is_open());

    // Write header: Number of source and target vertices.
    // Just as a very basic way to verify compatibility when loading this map.
    file << _source.n_vertices() << "\n";
    file << _target.n_vertices() << "\n";

    // Write map elements.
    for (const auto& source_vh : _source.vertices())
    {
        const BarycentricPoint& bary = _vtpm[source_vh];
        const HEH target_heh = bary.heh();
        const VH target_vh0 = _target.from_vertex_handle(target_heh);
        const VH target_vh1 = _target.to_vertex_handle(target_heh);

        file << target_vh0.idx() << " ";
        file << target_vh1.idx() << " ";
        file << bary.alpha() << " ";
        file << bary.beta() << "\n";
    }

    ISM_INFO("Wrote " << _path);
}

VertexToPointMap read_vertex_to_point_map(
        const std::string& _path,
        const TriMesh& _source,
        const TriMesh& _target,
        const bool _throw_if_not_exist)
{
    auto file = open_file_read(_path, _throw_if_not_exist);

    int expected_source_vertices = -1;
    int expected_target_vertices = -1;
    file >> expected_source_vertices;
    file >> expected_target_vertices;
    ISM_ASSERT_EQ(expected_source_vertices, _source.n_vertices());
    ISM_ASSERT_EQ(expected_target_vertices, _target.n_vertices());

    VertexToPointMap result(_source);
    for (int source_vi = 0; source_vi < expected_source_vertices; ++source_vi)
    {
        int target_vi0 = -1;
        int target_vi1 = -1;
        double alpha = NAN_DOUBLE;
        double beta = NAN_DOUBLE;

        ISM_ASSERT(file.good());
        file >> target_vi0 >> target_vi1 >> alpha >> beta;

        ISM_ASSERT_GEQ(target_vi0, 0);
        ISM_ASSERT_GEQ(target_vi1, 0);
        ISM_ASSERT_L(target_vi0, expected_target_vertices);
        ISM_ASSERT_L(target_vi1, expected_target_vertices);
        ISM_ASSERT_FINITE(alpha);
        ISM_ASSERT_FINITE(beta);

        const VH target_vh0 = _target.vertex_handle(target_vi0);
        const VH target_vh1 = _target.vertex_handle(target_vi1);
        const HEH target_heh = _target.find_halfedge(target_vh0, target_vh1);
        ISM_ASSERT(target_heh.is_valid());

        const VH source_vh = _source.vertex_handle(source_vi);
        const BarycentricPoint bary(target_heh, alpha, beta, _target);
        result[source_vh] = bary;
    }

    ISM_INFO("Read " << _path);
    return result;
}

MatXd read_matrix(
        const fs::path& _path)
{
    auto file = open_file_read(_path, true);

    int rows, cols;
    file >> rows >> cols;

    MatXd result(rows, cols);
    for (int row = 0; row < rows; ++row)
    {
        for (int col = 0; col < cols; ++col)
        {
            double v;
            file >> v;
            result(row, col) = v;
        }
    }
    return result;
}

template <typename T>
void write_matrix(
        const MatX<T>& _M,
        const fs::path& _path,
        const bool _overwrite,
        const bool _write_header)
{
    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }
    make_file_directory(_path);

    std::ofstream file(_path);
    ISM_ASSERT(file.is_open());

    if (_write_header)
        file << _M.rows() << " " << _M.cols() << "\n";
    for (int row = 0; row < _M.rows(); ++row)
    {
        for (int col = 0; col < _M.cols(); ++col)
        {
            file << _M(row, col);
            if (col == _M.cols() - 1)
                file << "\n";
            else
                file << " ";
        }
    }
    ISM_INFO("Wrote " << _path);
}
template void write_matrix(const MatX<int>&, const fs::path&, const bool, const bool);
template void write_matrix(const MatX<double>&, const fs::path&, const bool, const bool);

template <typename HandleT>
ExternalProperty<HandleT, double> read_property(
        const fs::path& _path,
        const TriMesh& _mesh,
        const bool _throw_if_not_exist)
{
    auto file = open_file_read(_path, _throw_if_not_exist);
    if (!file.is_open())
        return ExternalProperty<HandleT, double>();

    ExternalProperty<HandleT, double> data(_mesh);

    std::string line;
    int i = 0;
    while (std::getline(file, line))
    {
        // Cut off comments and trim
        remove_comment(line, "#");
        trim(line);

        if (line.empty())
            continue;

        data[HandleT(i)] = std::stod(line);
        ++i;
    }

    ISM_ASSERT_EQ(i, (int)data.size());
    ISM_INFO("Read " << _path);

    return data;
}
template ExternalProperty<VH, double> read_property(const fs::path&, const TriMesh&, const bool);
template ExternalProperty<EH, double> read_property(const fs::path&, const TriMesh&, const bool);
template ExternalProperty<HEH, double> read_property(const fs::path&, const TriMesh&, const bool);
template ExternalProperty<FH, double> read_property(const fs::path&, const TriMesh&, const bool);

template <typename HandleT>
void write_property(
        const ExternalProperty<HandleT, double>& _prop,
        const fs::path& _path,
        const bool _overwrite)
{
    if (file_exists(_path) && !_overwrite)
    {
        ISM_ERROR(_path << " already exists. Not overwriting!");
        return;
    }
    make_file_directory(_path);

    std::ofstream file(_path);
    file << _prop.as_eigen().format(Eigen::IOFormat(Eigen::FullPrecision));

    ISM_INFO("Wrote " << _path);
}
template void write_property(const ExternalProperty<VH, double>&, const fs::path&, const bool);
template void write_property(const ExternalProperty<EH, double>&, const fs::path&, const bool);
template void write_property(const ExternalProperty<HEH, double>&, const fs::path&, const bool);
template void write_property(const ExternalProperty<FH, double>&, const fs::path&, const bool);

std::string folder_name_from_time()
{
    auto t = std::time(nullptr);

    std::stringstream s;
    s << std::put_time(std::localtime(&t), "%Y_%m_%d_%H_%M_%S");

    return s.str();
}

std::string pad_integer(
        const int _i,
        const int _pad)
{
    std::ostringstream s;
    s << std::setw(_pad) << std::setfill('0') << _i;

    return s.str();
}

bool flag(const char* _flag, int _argc, char** _argv)
{
    for (int i = 1; i < _argc; ++i)
    {
        if (strcmp(_argv[i], _flag) == 0)
            return true;
    }

    return false;
}

}

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API