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/baptiste-genest/NESOTS
01 June 2024, 14:04:06 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    No releases to show
  • a4c74a1
  • /
  • SphericalUniformization.cpp
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:134ab9e2f09df6a4871e6bd2415d4cf6cc9a4f29
origin badgedirectory badge
swh:1:dir:a4c74a18ecd8e9e9ad93b6991435dc83ad5226bd
origin badgerevision badge
swh:1:rev:54d404d6d5dc3637ec2ac16a4e12bc1ebdf0eea2
origin badgesnapshot badge
swh:1:snp:56d5c0f212d094c22ee55ee1b5af34fc75a3956e

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
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: 54d404d6d5dc3637ec2ac16a4e12bc1ebdf0eea2 authored by Baptiste GENEST on 30 April 2024, 07:09:33 UTC
Update README.md
Tip revision: 54d404d
SphericalUniformization.cpp
#include "SphericalUniformization.h"


namespace CEPS {


SphericalUniformizationResult
hemisphericalUniformize(ManifoldSurfaceMesh& mesh, VertexPositionGeometry& geo,
                        bool viz) {
    // TODO: double mesh
    throw_verbose_runtime_error("Not implemented yet");
}

SphericalUniformizationResult sphericalUniformize(ManifoldSurfaceMesh& mesh,
                                                  VertexPositionGeometry& geo,
                                                  Vertex vInfinity, bool viz,
                                                  bool verbose) {
    using ImplementationDetails::computeCommonRefinementAndMatrix;
    using ImplementationDetails::layOutTriangulation;
    using ImplementationDetails::mobiusCenter;

    // ==========================================
    //      Initialize all of the meshes
    // ==========================================
    PartiallyDecoratedTriangulation Ta(mesh, geo);
    VertexData<Vector3> vertexPositions =
        geo.inputVertexPositions.reinterpretTo(*Ta.mesh);

    // TODO: filter front faces
    std::set<Face> frontFaces;
    for (Face f : Ta.mesh->faces()) frontFaces.insert(f);

    // Copy Ta into Tb and then flip to intrinsic Delaunay
    PartiallyDecoratedTriangulation Tb(Ta, false);
    Tb.flipToDelaunay(GeometryType::EUCLIDEAN);

    // Copy Tb into Tc
    PartiallyDecoratedTriangulation Tc(Tb, true);

    if (vInfinity == Vertex()) {
        VectorHeatMethodSolver solver(*Tc.geo, 1);
        Tc.geo->requireVertexDualAreas();
        vInfinity =
            findCenter(*Tc.mesh, *Tc.geo, solver, Tc.geo->vertexDualAreas, 1)
                .nearestVertex();
        Tc.geo->unrequireVertexDualAreas();
    }

    VertexData<double> distancesToHorocycle =
        Tc.distanceOfHorocyclesTo(vInfinity);

    if (viz) {
        auto psMesh = polyscope::getSurfaceMesh("input_mesh");
        psMesh->addVertexCountQuantity(
                    "vInfinity", std::vector<std::pair<size_t, int>>{
                        std::make_pair(vInfinity.getIndex(), 1)});
        psMesh->addVertexScalarQuantity("hyperbolic distance",
                        distancesToHorocycle);
    }


    // ==========================================
    //     Compute uniformizing scale factors
    // ==========================================
    if (verbose) std::cout << "Computing optimal scale factors..." << std::endl;
    PetscWrapper optimizer(Tc, distancesToHorocycle, vInfinity.getIndex(),
               std::numeric_limits<double>::infinity());

    // Set up petsc options
    std::vector<char*> argv;
    argv.emplace_back(
                const_cast<char*>("-tao_monitor")); // important for some reason
    argv.emplace_back(const_cast<char*>("-tao_type"));
    argv.emplace_back(const_cast<char*>("bnls"));
    // argv.emplace_back(const_cast<char*>("-citations"));
    // argv.emplace_back(const_cast<char*>("petsc_citations.txt"));

    std::clock_t start = std::clock();
    double duration;

    bool converged = optimizer.uniformize(argv.size(), argv.data());
    if (verbose) std::cout << "\t...done" << std::endl;

    if (!converged) {
        std::cout << "Error: optimizer failed to converge" << vendl;
    }

    duration = (std::clock() - start) / (double)CLOCKS_PER_SEC;

    // ==========================================
    //              Map to sphere
    // ==========================================
    if (verbose)
        std::cout << "Computing centered map to sphere..." << std::endl;
    // Lay out in plane
    VertexData<Vector2> planeCoords = layOutTriangulation(Tc, vInfinity);

    // Project to sphere
    VertexData<Vector3> sphericalCoords(*Tc.mesh);
    auto stereographicProject = [](Vector2 v) -> Vector3 {
        double denom = 1 + v.x * v.x + v.y * v.y;
        return Vector3{2 * v.x / denom, 2 * v.y / denom,
                    (-1 + v.x * v.x + v.y * v.y) / denom};
    };

    for (Vertex v : Tc.mesh->vertices()) {
        sphericalCoords[v] = stereographicProject(planeCoords[v]);
    }
    sphericalCoords[vInfinity] = Vector3{0, 0, 1};


    // Apply Mobius centering
    Tb.geo->requireVertexDualAreas();
    VertexData<double> dualAreas =
            Tb.geo->vertexDualAreas.reinterpretTo(*Tc.mesh);
    sphericalCoords = mobiusCenter(*Tc.mesh, sphericalCoords, dualAreas);
    Tb.geo->unrequireVertexDualAreas();
    if (verbose) std::cout << "\t...done" << std::endl;

    // ==========================================
    //             Common Refinement
    // ==========================================
    if (verbose) std::cout << "Interpolating..." << std::endl;

    // Compute log scale factors for map from Tb -> sphere

    // sphere-inscribed edge lengths
    EdgeData<double> dstLen(*Tc.mesh);
    for (Edge e : Tc.mesh->edges()) {
        dstLen[e] = (sphericalCoords[e.firstVertex()] -
                sphericalCoords[e.secondVertex()])
                .norm();
    }

    VertexData<double> empiricalScaleFactors(*Tc.mesh, 0);
    const EdgeData<double>& srcLen = Tc.originalEdgeLengths;
    for (Vertex v : Tc.mesh->vertices()) {
        Halfedge he = v.halfedge();

        double srcL1   = srcLen[he.edge()];
        double srcLopp = srcLen[he.next().edge()];
        double srcL2   = srcLen[he.next().next().edge()];
        double dstL1   = dstLen[he.edge()];
        double dstLopp = dstLen[he.next().edge()];
        double dstL2   = dstLen[he.next().next().edge()];

        double expU = (dstL1 / srcL1) * (dstL2 / srcL2) / (dstLopp / srcLopp);

        empiricalScaleFactors[v] = log(expU);
    }
    Tc.setScaleFactors(empiricalScaleFactors);

    SphericalUniformizationResult result;
    std::tie(result.mesh, result.param, result.parentMap,
         result.interpolationMatrix) =
            computeCommonRefinementAndMatrix(Ta, Tb, Tc, vertexPositions,
                             sphericalCoords, frontFaces, verbose);
    if (verbose) std::cout << "\t...done" << std::endl;

    if (viz) {
        auto psSphere = polyscope::registerSurfaceMesh(
                    "sphereMesh", sphericalCoords, Tc.mesh->getFaceVertexList());
        psSphere->setEnabled(false);

        auto psCommonRefinement = polyscope::registerSurfaceMesh(
                    "CommonRefinement", result.mesh.vertexCoordinates,
                    result.mesh.polygons);
        auto q = polyscope::addProjectiveSphericalParameterizationQuantity(
                    *psCommonRefinement, "param", result.param);
        q->setEnabled(true);
    }

    std::cout << "EXPORT FILE " <<std::endl;
        std::ofstream outFile("/tmp/spherical-export.obj");
        std::ofstream outFileM("/tmp/CR-export.obj");
        for (Vertex v : Tc.mesh->vertices()) {
            auto p= sphericalCoords[v];
            outFile << "v " << p.x << " " << p.y << " " << p.z << std::endl;
            auto x = vertexPositions[v];
            outFileM << "v " << x.x << " " << x.y << " " << x.z << std::endl;
        }

        // Write faces
        for (auto face : Tc.mesh->getFaceVertexList())
        {
            outFile << "f";
            outFileM << "f";
            for (size_t ind : face) {
                outFile << " " << (ind + 1) ;
                outFileM << " " << (ind + 1) ;
            }
            outFile << std::endl;
            outFileM << std::endl;
        }


    return result;
}


static bool printedFNaN = false;
double f(PartiallyDecoratedTriangulation& tri, Face face) {
    double out = 0;
    tri.geo->requireCornerAngles();
    EdgeData<double>& lengths  = tri.geo->inputEdgeLengths;
    CornerData<double>& angles = tri.geo->cornerAngles;
    for (Halfedge he : face.adjacentHalfedges()) {
        double angle   = angles[he.next().next().corner()];
        double contrib = 0;
        contrib += angle * log(lengths[he.edge()]);
        contrib += lobachevsky(angle);

        if (std::isnan(contrib)) {
            if (!printedFNaN) {
                cerr << "f(Face) is NaN" << endl;
                cerr << "\t             angle: " << angle << endl;
                cerr << "\tlobachevsky(angle): " << lobachevsky(angle) << endl;
                cerr << "\t       log(length): " << log(lengths[he.edge()])
                        << endl;
                cerr << "\t edge lengths: ";
                for (Edge e : face.adjacentEdges()) {
                    cerr << lengths[e] << " ";
                }
                cerr << endl;
                printedFNaN = true;
            }
            return std::numeric_limits<double>::infinity();
            // my_assert(false, "objective error");
        }

        out += contrib;
    }
    if (out != out) out = std::numeric_limits<double>::infinity();
    return out;
}

double sphericalObjective(PartiallyDecoratedTriangulation& tri) {

    EdgeData<double>& lengths = tri.geo->inputEdgeLengths;

    // Vertex vInfinity = tri.mesh->vertex(655);
    // for (Vertex w : vInfinity.adjacentVertices()) {
    //     WATCH(w);
    //     std::cout << "\t" << w << ":\t" << 2 * PI * tri.logScaleFactors[w]
    //               << vendl;
    //     for (Edge e : w.adjacentEdges()) {
    //         if (tri.finite(e)) {
    //             std::cout << "\t" << src(e) << "-" << dst(e) << ":\t"
    //                       << 2 * PI * log(lengths[e]) << vendl;
    //         }
    //     }
    //     for (Face face : w.adjacentFaces()) {
    //         if (tri.finite(face)) {
    //             std::cout << "\t" << face.halfedge().vertex() << "-"
    //                       << face.halfedge().next().vertex() << "-"
    //                       << face.halfedge().next().next().vertex() << ":\t"
    //                       << 2 * f(tri, face) << vendl;
    //         }
    //     }
    // }

    double objective = 0;
    size_t iF        = 0;
    for (Face face : tri.mesh->faces()) {
        if (tri.finite(face)) {
            objective += 2 * f(tri, face);
            // if (iF++ < 5)
            //     std::cout << "f" << iF << ":\t" << 2 * f(tri, face) << vendl;
        }
    }

    size_t iE = 0;
    for (Edge e : tri.mesh->edges()) {
        if (tri.finite(e)) {
            objective -= PI * 2 * log(lengths[e]);
            // if (iE++ < 5)
            //     std::cout << "e" << iE << ":\t" << PI * 2 * log(lengths[e])
            //               << vendl;
        }
    }

    size_t iV = 0;
    for (Vertex v : tri.mesh->vertices()) {
        if (tri.finite(v)) {
            objective += 2 * PI * tri.logScaleFactors[v];
            // if (iV++ < 5)
            //     std::cout << "v" << iV << ":\t"
            //               << 2 * PI * tri.logScaleFactors[v] << vendl;
        }
    }

    // polyscope::getSurfaceMesh("input_mesh")
    //     ->addVertexScalarQuantity("u", tri.logScaleFactors);
    // polyscope::show();
    // throw_verbose_runtime_error("nyah");

    return objective;
}

VertexData<double>
sphericalGradientVertexData(PartiallyDecoratedTriangulation& tri) {
    VertexData<double> grad(*tri.mesh);
    VertexData<double> angleSums = tri.angleSums();
    for (Vertex v : tri.mesh->vertices()) {
        if (tri.finite(v)) {
            grad[v] = -angleSums[v] + PI * (tri.degF(v) - tri.degE(v) + 2);
        } else {
            grad[v] = 0;
        }
    }
    return grad;
}

Vector<double> sphericalGradient(PartiallyDecoratedTriangulation& tri) {
    return sphericalGradientVertexData(tri).toVector();
}

SparseMatrix<double> sphericalHessian(PartiallyDecoratedTriangulation& tri) {
    return tri.partialCotanLaplacian();
}

namespace ImplementationDetails {
VertexData<Vector2> layOutTriangulation(PartiallyDecoratedTriangulation& tri,
                    Vertex vInfinity) {

    // Build Laplacian for mesh with vInfinity removed
    size_t nV               = tri.mesh->nVertices();
    VertexData<size_t> vIdx = tri.mesh->getVertexIndices();

    tri.geo->requireHalfedgeCotanWeights();
    std::vector<Eigen::Triplet<std::complex<double>>> TL;
    TL.emplace_back(vIdx[vInfinity], vIdx[vInfinity], 1);
    for (Halfedge he : tri.mesh->interiorHalfedges()) {
        if (!tri.finite(he.face())) continue;

        size_t iHead = vIdx[he.tipVertex()];
        size_t iTail = vIdx[he.tailVertex()];

        std::complex<double> weight = tri.geo->halfedgeCotanWeights[he];

        TL.emplace_back(iTail, iTail, weight);
        TL.emplace_back(iHead, iHead, weight);
        TL.emplace_back(iTail, iHead, -weight);
        TL.emplace_back(iHead, iTail, -weight);
    }
    for (size_t iC = 0; iC < nV; ++iC) TL.emplace_back(iC, iC, 1e-12);
    SparseMatrix<std::complex<double>> L(nV, nV);
    L.setFromTriplets(std::begin(TL), std::end(TL));

    // Build the mass matrix
    std::vector<Eigen::Triplet<std::complex<double>>> TM;
    tri.geo->requireFaceAreas();
    TM.emplace_back(vIdx[vInfinity], vIdx[vInfinity], 1);
    for (Face f : tri.mesh->faces()) {
        if (!tri.finite(f)) continue;
        std::complex<double> weight = tri.geo->faceAreas[f] / 3.;
        for (Vertex v : f.adjacentVertices()) {
            TM.emplace_back(vIdx[v], vIdx[v], weight / 3.);
        }
    }
    SparseMatrix<std::complex<double>> M(nV, nV);
    M.setFromTriplets(std::begin(TM), std::end(TM));

    // Build the area term
    std::complex<double> i(0, 1);
    std::vector<Eigen::Triplet<std::complex<double>>> TA;

    for (Halfedge heSpoke : vInfinity.outgoingHalfedges()) {
        Halfedge he = heSpoke.next().twin();
        size_t j    = vIdx[he.tipVertex()];
        size_t k    = vIdx[he.tailVertex()];

        TA.emplace_back(j, k, i * 0.25);
        TA.emplace_back(k, j, i * -0.25);
    }
    SparseMatrix<std::complex<double>> A(nV, nV);
    A.setFromTriplets(std::begin(TA), std::end(TA));

    SparseMatrix<std::complex<double>> EC = 0.5 * L - A;

    Vector<std::complex<double>> pos = ImplementationDetails::eig(EC, M, 1e-8);

    VertexData<Vector2> positions(*tri.mesh);
    for (Vertex v : tri.mesh->vertices()) {
        Vector2 cPos = Vector2::fromComplex(pos(vIdx[v]));
        positions[v] = cPos;
    }

    return positions;
}

Eigen::Vector3d toEigen(const Vector3& v) {
    Eigen::Vector3d ret;
    ret << v.x, v.y, v.z;
    return ret;
}

Vector3 fromEigen(const Eigen::Vector3d& v) {
    return Vector3{v[0], v[1], v[2]};
}

// Returns vw^T
Eigen::Matrix3d outer(const Vector3& v, const Vector3& w) {
    return toEigen(v) * toEigen(w).transpose();
}

VertexData<Vector3> mobiusCenter(ManifoldSurfaceMesh& mesh,
                 const VertexData<Vector3>& positions,
                 VertexData<double> area, bool verbose) {

    double surfaceArea = area.toVector().sum();
    area /= surfaceArea;

    Vector3 centerOfMass;
    VertexData<Vector3> centeredPositions = positions;

    auto evalStep = [&](Vector3 c) {
        Vector3 newCenter{0, 0, 0};
        for (Vertex v : mesh.vertices()) {
            Vector3 p    = centeredPositions[v];
            Vector3 newP = (1 - norm2(c)) * (p + c) / norm2(p + c) + c;
            newCenter += area[v] * newP;
        }
        return norm(newCenter);
    };
    auto takeStep = [&](Vector3 c) {
        Vector3 newCenter{0, 0, 0};
        for (Vertex v : mesh.vertices()) {
            Vector3 p            = centeredPositions[v];
            centeredPositions[v] = (1 - norm2(c)) * (p + c) / norm2(p + c) + c;
            newCenter += area[v] * centeredPositions[v];
        }
        return newCenter;
    };

    centerOfMass    = takeStep(Vector3{0, 0, 0});
    double stepSize = 1;
    while (norm(centerOfMass) > 1e-3) {
        Eigen::Matrix3d J = Eigen::Matrix3d::Zero();
        Eigen::Matrix3d I = Eigen::Matrix3d::Identity();
        for (Vertex v : mesh.vertices()) {
            Vector3 p = centeredPositions[v];
            J += 2 * area[v] * (I - outer(p, p));
        }

        Vector3 step = fromEigen(-J.inverse() * toEigen(centerOfMass));

        double oldObjective = norm(centerOfMass);


        // https://en.wikipedia.org/wiki/Golden-section_search
        double invphi  = 0.5 * (sqrt(5.) - 1); // 1/phi
        double invphi2 = 0.5 * (3 - sqrt(5.)); // 1/phi^2
        double a       = 0;
        double b       = 1;

        double h  = b - a;
        double c  = a + invphi2 * h;
        double d  = a + invphi * h;
        double fc = evalStep(c * step);
        double fd = evalStep(d * step);

        while (abs(c - d) > 1e-8) {
            if (fc < fd) {
                b  = d;
                d  = c;
                fd = fc;
                h  = invphi * h;
                c  = a + invphi2 * h;
                fc = evalStep(c * step);
            } else {
                a  = c;
                c  = d;
                fc = fd;
                h  = invphi * h;
                d  = a + invphi * h;
                fd = evalStep(d * step);
            }
        }
        double stepSize;
        if (fc < fd) {
            stepSize = (a + d) / 2;
        } else {
            stepSize = (c + b) / 2;
        }

        centerOfMass = takeStep(stepSize * step);

        if (verbose) {
            cout << "mu: " << centerOfMass << "\tnorm: " << norm(centerOfMass)
                 << "\tstepSize: " << stepSize << endl;
        }
    }
    return centeredPositions;
}

std::tuple<SimplePolygonMesh, std::vector<std::array<double, 4>>,
VertexData<int>, SparseMatrix<double>>
computeCommonRefinementAndMatrix(Triangulation& Ta, Triangulation& Tb,
                 Triangulation& Tc,
                 const VertexData<Vector3>& initialPositions,
                 const VertexData<Vector3>& sphericalPositions,
                 const std::set<Face>& frontFaces,
                 bool verbose) {

    using ImplementationDetails::computeCommonRefinement;
    using ImplementationDetails::Doublet;
    using ImplementationDetails::SparseVector;

    CornerData<DirectSum<Vector3, double>> homogSphericalPositions(*Tc.mesh);
    for (Corner c : Tc.mesh->corners()) {
        homogSphericalPositions[c] =
                DirectSum<Vector3, double>(sphericalPositions[c.vertex()], 1);
    }

    std::vector<char> isFrontFace(Ta.mesh->nFaces(), false);
    if (frontFaces.empty()) {
        for (size_t iF = 0; iF < isFrontFace.size(); ++iF) {
            isFrontFace[iF] = true;
        }
    } else {
        FaceData<size_t> fIdx = Ta.mesh->getFaceIndices();
        for (Face f : frontFaces) {
            isFrontFace[fIdx[f]] = true;
        }
    }

    // We compute the interpolation matrix from Ta to the common refinement by
    // linearly interpolating the |Va|x|Va| identity matrix.
    // More explicitly, we associate a row vector to each vertex of Ta which
    // contains a 1 at that vertex's index. Linearly interpolating these row
    // vectors to new vertices of the common refinement produces the desired
    // interpolation matrix.
    // We store these vectors as custom sparse vectors (defined in
    // CommonRefinement.h)
    VertexData<size_t> vIdxA = Ta.mesh->getVertexIndices();
    VertexData<SparseVector> trivialInterpolation(*Ta.mesh);
    for (Vertex v : Ta.mesh->vertices())
        trivialInterpolation[v].doublets.push_back(Doublet{vIdxA[v], 1});

    SimplePolygonMesh commonRefinement;
    std::vector<SparseVector> refinedInterpolation;
    std::vector<DirectSum<Vector3, double>> refinedHomogSphericalPositions;
    VertexData<int> refinedVertices;
    std::tie(commonRefinement, refinedInterpolation,
         refinedHomogSphericalPositions, refinedVertices) =
            computeCommonRefinement(Ta, Tb, Tc, trivialInterpolation,
                        homogSphericalPositions, isFrontFace, verbose);

    // Eventually we want to strip unused vertices, since for meshes where we
    // filter out faces (i.e. doubled meshes), we are left with unused vertices
    // It's most convenient to strip them out here before we build the
    // interpolation matrix
    // We essentially just need to strip out the corresponding rows of the
    // interpolation matrix
    // So we store each vertex's index in the x coordinate of its position,
    // strip unused vertices, and then build the interpolation matrix, using
    // these x coordinates to select the correct rows
    size_t old_nV = commonRefinement.vertexCoordinates.size();
    for (size_t iV = 0; iV < old_nV; ++iV) {
        commonRefinement.vertexCoordinates[iV].x = iV;
    }
    commonRefinement.stripUnusedVertices();
    size_t nV = commonRefinement.vertexCoordinates.size(); // update nV

    // Compute vertex positions on the common refinement by interpolating the
    // original vertex positions using the interpolation matrix
    std::vector<Vector3> refinedVertexPositions;
    for (const Vector3& v : commonRefinement.vertexCoordinates) {
        size_t row = std::round(v.x);
        Vector3 pos{0, 0, 0};
        for (const Doublet& d : refinedInterpolation[row].doublets) {
            pos += d.val * initialPositions[Ta.mesh->vertex(d.col)];
        }
        refinedVertexPositions.push_back(pos);
    }

    // Build the interpolation matrix
    std::vector<Eigen::Triplet<double>> tripletList;
    for (size_t row = 0; row < nV; ++row) {
        size_t iV = std::round(commonRefinement.vertexCoordinates[row].x);
        for (const Doublet& d : refinedInterpolation[iV].doublets) {
            tripletList.emplace_back(row, d.col, d.val);
        }
    }
    SparseMatrix<double> interpolationMatrix(
                commonRefinement.vertexCoordinates.size(), Ta.mesh->nVertices());
    interpolationMatrix.setFromTriplets(std::begin(tripletList),
                        std::end(tripletList));

    //== Update the vertex index map (indices have shifted since we stripped
    // unused vertices)
    // Map from common refinement -> Ta, using old indices
    // Vertices which are not present in Ta have their parent set to Vertex()
    std::vector<Vertex> parentVertex(old_nV, Vertex());
    for (Vertex v : Ta.mesh->vertices()) parentVertex[refinedVertices[v]] = v;

    // Update refinedVertices array with new indices
    for (size_t new_iV = 0; new_iV < nV; new_iV++) {
        size_t old_iV =
                std::round(commonRefinement.vertexCoordinates[new_iV].x);
        Vertex parent = parentVertex[old_iV];
        if (parent != Vertex()) {
            refinedVertices[parent] = new_iV;
        }
    }

    // Mark vertices of Ta which don't appear in the common refinement
    for (Vertex v : Ta.mesh->vertices()) {
        if (refinedVertices[v] >= static_cast<int>(nV)) {
            // invalid index - must not appear
            refinedVertices[v] = -1;
        } else {
            int new_iV = refinedVertices[v];
            int old_iV =
                    std::round(commonRefinement.vertexCoordinates[new_iV].x);
            if (v != parentVertex[old_iV]) {
                refinedVertices[v] = -1;
            }
        }
    }

    commonRefinement.vertexCoordinates = refinedVertexPositions;

    // Convert from CornerData to VertexData, and from DirectSum to std::array
    std::vector<std::array<double, 4>> niceRefinedSphereCoords(nV);
    size_t iC = 0;
    for (const std::vector<size_t>& face : commonRefinement.polygons) {
        for (size_t iV : face) {
            const DirectSum<Vector3, double>& q =
                    refinedHomogSphericalPositions[iC];
            niceRefinedSphereCoords[iV] = {-q.x.x, q.x.y, q.x.z, q.y};
            iC++;
        }
    }

    return std::tie(commonRefinement, niceRefinedSphereCoords, refinedVertices,
            interpolationMatrix);
}

} // namespace ImplementationDetails

} // namespace CEPS

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