959 lines
30 KiB
C++
959 lines
30 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Paella
|
|
// Copyright (C) 2015 - Thomas FORGIONE, Emilie JALRAS, Marion LENFANT, Thierry MALON, Amandine PAILLOUX
|
|
// Authors :
|
|
// Thomas FORGIONE
|
|
// Emilie JALRAS
|
|
// Marion LENFANT
|
|
// Thierry MALON
|
|
// Amandine PAILLOUX
|
|
//
|
|
// This file is part of the project Paella
|
|
// This software is provided 'as-is', without any express or implied warranty.
|
|
// In no event will the authors be held liable for any damages arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it freely,
|
|
// subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented;
|
|
// you must not claim that you wrote the original software.
|
|
// If you use this software in a product, an acknowledgment
|
|
// in the product documentation would be appreciated but is not required.
|
|
//
|
|
// 2. Altered source versions must be plainly marked as such,
|
|
// and must not be misrepresented as being the original software.
|
|
//
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <cmath>
|
|
#include <list>
|
|
#include <eigen3/Eigen/Dense>
|
|
#include <algorithm>
|
|
|
|
#include "Meshing/Skeleton3D.hpp"
|
|
#include "Geometry/Point.hpp"
|
|
|
|
namespace pae
|
|
{
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template<typename T>
|
|
std::ostream& operator<<(std::ostream& out, std::pair<std::vector<std::list<geo::Point<T>>>, std::vector<std::list<geo::Point<T>>>> const& pair)
|
|
{
|
|
unsigned int i = 0;
|
|
out << "up_points_vector_list" << std::endl;
|
|
for (auto const& l : pair.first)
|
|
{
|
|
out << "circle_" << i << std::endl;
|
|
for (auto const& p : l)
|
|
{
|
|
out << p.first << " ";
|
|
}
|
|
out << std::endl;
|
|
i++;
|
|
}
|
|
|
|
out << std::endl << "Second list" << std::endl;
|
|
i = 0;
|
|
for (auto const& l : pair.second)
|
|
{
|
|
|
|
out << "circle_" << i << std::endl;
|
|
for (auto const& p : l)
|
|
{
|
|
out << p.first << " ";
|
|
}
|
|
out << std::endl;
|
|
i++;
|
|
}
|
|
|
|
|
|
return out;
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
template<typename... Types>
|
|
Skeleton3D<Types...>::Skeleton3D() : splines{}
|
|
{
|
|
|
|
}
|
|
|
|
template<typename... Types>
|
|
bool Skeleton3D<Types...>::loadFromFile(std::string const& path)
|
|
{
|
|
std::fstream file{path};
|
|
|
|
if (!file)
|
|
{
|
|
std::cerr << "Warning : couldn't load Skeleton from file " << path << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::stringstream stream;
|
|
std::string line;
|
|
bool alreadyFoundL = false;
|
|
geo::Spline<Types...> currentSpline;
|
|
|
|
while (std::getline(file, line))
|
|
{
|
|
if (line.size() == 0)
|
|
continue;
|
|
|
|
char first;
|
|
stream.clear();
|
|
stream.str(line);
|
|
|
|
stream >> first;
|
|
|
|
switch (first)
|
|
{
|
|
case 'd':
|
|
{
|
|
// Add the current spline
|
|
splines.push_back(currentSpline);
|
|
|
|
// Prepare the new one
|
|
currentSpline = geo::Spline<Types...>{};
|
|
|
|
// Set the degree of the spline
|
|
int i;
|
|
stream >> i;
|
|
currentSpline.degree = i;
|
|
|
|
break;
|
|
}
|
|
|
|
case 'n':
|
|
{
|
|
float x;
|
|
while (stream >> x)
|
|
{
|
|
currentSpline.nodes.push_back(x);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 'p':
|
|
{
|
|
// Add control point to the spline
|
|
currentSpline.addControlPoint(line.substr(1));
|
|
break;
|
|
}
|
|
|
|
case 'l':
|
|
{
|
|
if (!alreadyFoundL)
|
|
{
|
|
splines.push_back(currentSpline);
|
|
alreadyFoundL = true;
|
|
}
|
|
|
|
float size_junction;
|
|
stream >> size_junction;
|
|
|
|
unsigned int x;
|
|
|
|
junctions.push_back(Junction{});
|
|
Junction& j = junctions[junctions.size()-1];
|
|
|
|
for (unsigned int i = 0; i<size_junction; i++)
|
|
{
|
|
stream >> x;
|
|
j.push_back(std::make_pair(x-1,0.0f));
|
|
}
|
|
|
|
float y;
|
|
for (unsigned int i = 0; i<size_junction; i++)
|
|
{
|
|
stream >> y;
|
|
j[i].second = y;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!alreadyFoundL)
|
|
{
|
|
splines.push_back(currentSpline);
|
|
}
|
|
|
|
// Remove the first spline that is empty
|
|
splines.erase(splines.begin());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
template<typename... T>
|
|
void Skeleton3D<T...>::meshJunctions(geo::Mesh& mesh, std::vector<std::vector<geo::Circle<float>>> const& splines_circles) const
|
|
{
|
|
for(unsigned int i = 0; i < junctions.size(); i++)
|
|
{
|
|
geo::Vector3<float> junction_point; // sphere's center of the junction point
|
|
float junction_radius; // sphere's radius
|
|
|
|
// Get the junction elements
|
|
auto spline = splines[junctions[i][0].first];
|
|
auto tuple = spline(junctions[i][0].second);
|
|
|
|
junction_point = std::get<0>(tuple);
|
|
junction_radius = std::get<1>(tuple);
|
|
|
|
|
|
std::vector<geo::Circle<float>> junction_circles; // characteristic circles from the splines of the junction
|
|
for(unsigned int j = 0; j<junctions[i].size(); j++)
|
|
{
|
|
unsigned int indSpline = junctions[i][j].first;
|
|
float t = junctions[i][j].second;
|
|
auto spline_circles = splines_circles[indSpline];
|
|
|
|
if (t==0.0f)
|
|
{
|
|
junction_circles.push_back(spline_circles[0]);
|
|
}
|
|
else
|
|
{
|
|
junction_circles.push_back(spline_circles[spline_circles.size()-1]);
|
|
}
|
|
}
|
|
meshJunction<float>(mesh,junction_point,junction_radius, junction_circles);
|
|
}
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
geo::Vector<T,4> meshJunction(geo::Mesh& mesh, geo::Vector3<T> const& junction_point, float junction_radius, std::vector<geo::Circle<T>> const& junction_circles)
|
|
{
|
|
std::vector<geo::Vector3<float>> centerPoints;
|
|
|
|
for (auto const& circle : junction_circles)
|
|
{
|
|
centerPoints.push_back(circle.center);
|
|
}
|
|
// Find the medium plane that cut the circles in two
|
|
geo::Vector<float,4> plane = detail::closestPlane<float>(centerPoints);
|
|
|
|
// Compute list of points upside the plane and downside
|
|
auto sorted_points = detail::sortedPoints<float>(plane, junction_circles);
|
|
|
|
geo::Vector3<float> direction{plane.x(),plane.y(),plane.z()};
|
|
direction /= direction.norm();
|
|
geo::Vector3<float> up_point_proj = junction_point - direction*junction_radius;
|
|
geo::Vector3<float>down_point_proj = junction_point + direction*junction_radius;
|
|
|
|
mesh.vertices.push_back(up_point_proj);
|
|
mesh.vertices.push_back(down_point_proj);
|
|
|
|
// choose the indice of the two new points
|
|
unsigned int ind_up = mesh.vertices.size()-2;
|
|
unsigned int ind_down = mesh.vertices.size()-1;
|
|
|
|
// mesh the up part of the sphere
|
|
for (auto const& up_points : sorted_points.first)
|
|
{
|
|
auto last = std::end(up_points);
|
|
last--;
|
|
|
|
for(auto it = std::begin(up_points), end = std::end(up_points); it != end; ++it)
|
|
{
|
|
if (it != last)
|
|
{
|
|
auto cp = it;
|
|
cp ++;
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{it->first,cp->first,ind_up},
|
|
junction_point);
|
|
}
|
|
}
|
|
}
|
|
|
|
// mesh the down part of the sphere
|
|
for (auto const& down_points : sorted_points.second)
|
|
{
|
|
auto last = std::end(down_points);
|
|
last--;
|
|
|
|
for(auto it = std::begin(down_points), end = std::end(down_points); it != end; ++it)
|
|
{
|
|
if (it != last)
|
|
{
|
|
auto cp = it;
|
|
cp ++;
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{it->first,cp->first,ind_down},
|
|
junction_point);
|
|
}
|
|
}
|
|
}
|
|
|
|
// connect the four points around the plane
|
|
|
|
auto points_c1_up = sorted_points.first.at(0);
|
|
auto points_c2_up = sorted_points.first.at(1);
|
|
|
|
auto points_c1_down = sorted_points.second.at(0);
|
|
auto points_c2_down = sorted_points.second.at(1);
|
|
|
|
auto p_c1_up = points_c1_up.front();
|
|
auto p_c1_up1 = points_c1_up.back();
|
|
|
|
auto p_c2_up = points_c2_up.front();
|
|
auto p_c2_up1 = points_c2_up.back();
|
|
|
|
// if (points_c1_up.size() == 0 || points_c2_up.size() == 0 || points_c1_down.size() == 0 || points_c2_down.size() == 0)
|
|
// {
|
|
// throw std::runtime_error("Splitting the circles didn't work : a circle had no points");
|
|
// }
|
|
|
|
std::vector<std::pair<geo::Point<float>, geo::Point<float>>> pairs{
|
|
{p_c1_up,p_c2_up},
|
|
{p_c1_up,p_c2_up1},
|
|
{p_c1_up1,p_c2_up},
|
|
{p_c1_up1,p_c2_up1}
|
|
};
|
|
|
|
auto min_pair = std::min_element(std::begin(pairs), std::end(pairs),
|
|
[] (std::pair<geo::Point<T>, geo::Point<T>> const& points,
|
|
std::pair<geo::Point<T>, geo::Point<T>> const& points2) {
|
|
|
|
return (points.first.second-points.second.second).norm2() <
|
|
(points2.first.second-points2.second.second).norm2();
|
|
|
|
});
|
|
|
|
unsigned int ind_i = std::distance(std::begin(pairs), min_pair);
|
|
|
|
std::pair<geo::Point<float>, geo::Point<float>> next_points;
|
|
|
|
unsigned int ind_p_c1_up = 0, ind_p_c2_up = 0, ind_p_c2_up1 = 0, ind_p_c1_up1 = 0, ind_p_c1_down = 0, ind_p_c1_down1 = 0, ind_p_c2_down = 0, ind_p_c2_down1 = 0;
|
|
|
|
std::cout << " ind_i = " << ind_i <<std::endl;
|
|
switch(ind_i)
|
|
{
|
|
case 0 :
|
|
{
|
|
ind_p_c1_up = p_c1_up.first;
|
|
ind_p_c2_up = p_c2_up.first;
|
|
ind_p_c1_down = points_c1_down.back().first;
|
|
ind_p_c2_down = points_c2_down.back().first;
|
|
|
|
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up, ind_p_c2_up, ind_p_c1_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_down, ind_p_c2_up, ind_p_c2_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up, ind_up, ind_p_c2_up},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_down, ind_p_c2_down, ind_down},
|
|
junction_point);
|
|
|
|
geo::Point<float> p_c2_down = points_c2_down.front();
|
|
next_points = std::make_pair(p_c2_up1, p_c2_down);
|
|
|
|
break;
|
|
}
|
|
case 1 :
|
|
{
|
|
ind_p_c1_up = p_c1_up.first;
|
|
ind_p_c1_down1 = points_c1_down.back().first;
|
|
ind_p_c2_up1 = p_c2_up1.first;
|
|
ind_p_c2_down = points_c2_down.front().first;
|
|
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up, ind_p_c2_up1, ind_p_c1_down1},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_down1, ind_p_c2_up1, ind_p_c2_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up, ind_up, ind_p_c2_up1},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_down, ind_p_c1_down1, ind_down},
|
|
junction_point);
|
|
|
|
geo::Point<float> p_c2_down1 = points_c2_down.back();
|
|
next_points = std::make_pair(p_c2_up,p_c2_down1);
|
|
|
|
break;
|
|
}
|
|
case 2 :
|
|
{
|
|
|
|
ind_p_c1_up1 = p_c1_up1.first;
|
|
ind_p_c1_down = points_c1_down.front().first;
|
|
ind_p_c2_up = p_c2_up.first;
|
|
ind_p_c2_down1 = points_c2_down.back().first;
|
|
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up1, ind_p_c2_up, ind_p_c1_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up1, ind_p_c1_down, ind_p_c2_down1},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up1, ind_up, ind_p_c2_up},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_down, ind_p_c2_down1, ind_down},
|
|
junction_point);
|
|
|
|
geo::Point<float> p_c2_down = points_c2_down.front();
|
|
next_points = std::make_pair(p_c2_up1, p_c2_down);
|
|
|
|
break;
|
|
}
|
|
case 3 :
|
|
{
|
|
|
|
|
|
ind_p_c2_up = p_c2_up1.first;
|
|
ind_p_c1_up = p_c1_up1.first;
|
|
ind_p_c1_down = points_c1_down.front().first;
|
|
ind_p_c2_down = points_c2_down.front().first;
|
|
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up, ind_p_c2_up, ind_p_c1_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_down, ind_p_c2_up, ind_p_c2_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_up, ind_up, ind_p_c2_up},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c1_down, ind_p_c2_down, ind_down},
|
|
junction_point);
|
|
|
|
auto p_c2_down1 = points_c2_down.back();
|
|
next_points = std::make_pair(p_c2_up,p_c2_down1);
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
for (unsigned int i = 1; i < sorted_points.first.size(); i++)
|
|
{
|
|
auto points_c3_up = sorted_points.first[(i+1)%sorted_points.first.size()];
|
|
auto points_c3_down = sorted_points.second[(i+1)%sorted_points.first.size()];
|
|
|
|
auto p_c3_up = points_c3_up.front();
|
|
auto p_c3_up1 = points_c3_up.back();
|
|
|
|
auto p_c3_down = points_c3_down.front();
|
|
auto p_c3_down1 = points_c3_down.back(); ind_p_c1_up1 = p_c1_up1.first;
|
|
ind_p_c1_down = points_c1_down.back().first;
|
|
|
|
|
|
auto p_c2_up = next_points.first;
|
|
auto p_c2_down = next_points.second;
|
|
|
|
float dist_1 = (p_c2_up.second - p_c3_up.second).norm2();
|
|
float dist_2 = (p_c2_up.second - p_c3_up1.second).norm2();
|
|
|
|
if (dist_1<dist_2)
|
|
{
|
|
unsigned int ind_p_c2_up = p_c2_up.first;
|
|
unsigned int ind_p_c2_down = p_c2_down.first;
|
|
|
|
unsigned int ind_p_c3_up = p_c3_up.first;
|
|
unsigned int ind_p_c3_down = p_c3_down1.first;
|
|
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_up, ind_p_c3_up, ind_p_c2_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_down, ind_p_c3_up, ind_p_c3_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_up, ind_up, ind_p_c3_up},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_down, ind_p_c3_down, ind_down},
|
|
junction_point);
|
|
|
|
next_points.first = p_c3_up1;
|
|
next_points.second = p_c3_down;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
unsigned int ind_p_c2_up = p_c2_up.first;
|
|
unsigned int ind_p_c2_down = p_c2_down.first;
|
|
|
|
unsigned int ind_p_c3_up = p_c3_up1.first;
|
|
unsigned int ind_p_c3_down = p_c3_down.first;
|
|
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_up, ind_p_c3_up, ind_p_c2_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_down, ind_p_c3_up, ind_p_c3_down},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_up, ind_up, ind_p_c3_up},
|
|
junction_point);
|
|
detail::pushFaceOriented(mesh,
|
|
geo::Vector3<unsigned int>{ind_p_c2_down, ind_p_c3_down, ind_down},
|
|
junction_point);
|
|
|
|
next_points.first = p_c3_up;
|
|
next_points.second = p_c3_down1;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return plane;
|
|
|
|
}
|
|
|
|
template<typename... T>
|
|
std::vector<std::pair<unsigned int, float>> Skeleton3D<T...>::findExtremities() const
|
|
{
|
|
std::vector<std::pair<unsigned int, float>> extremities;
|
|
std::vector<unsigned int> indexJunctions;
|
|
|
|
//construction of a vector containing indexes of a spline *2 + t
|
|
if (junctions.empty())
|
|
{
|
|
for (unsigned int i=0;i<splines.size();i++)
|
|
{
|
|
extremities.push_back(std::make_pair(i,0));
|
|
extremities.push_back(std::make_pair(i,1));
|
|
}
|
|
return extremities;
|
|
}
|
|
|
|
for (unsigned int i=0;i<junctions.size();i++)
|
|
{
|
|
for (unsigned int j=0;j<junctions[i].size();j++)
|
|
{
|
|
std::pair<unsigned int, float> extremSpline = junctions[i][j];
|
|
indexJunctions.push_back(extremSpline.first*2 + extremSpline.second);
|
|
}
|
|
}
|
|
|
|
std::sort(indexJunctions.begin(),indexJunctions.end());
|
|
|
|
unsigned int index=0;
|
|
for (unsigned int i=0;i<splines.size();i++)
|
|
{
|
|
if (i*2==indexJunctions[index])
|
|
{
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
extremities.push_back(std::make_pair(i,0));
|
|
}
|
|
if (((i*2)+1)==indexJunctions[index])
|
|
{
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
extremities.push_back(std::make_pair(i,1));
|
|
}
|
|
}
|
|
return extremities;
|
|
}
|
|
|
|
template<typename... T>
|
|
void Skeleton3D<T...>::meshExtremities(geo::Mesh& mesh, std::vector<std::vector<geo::Circle<float>>> const& splines_circles, unsigned int profondeur) const
|
|
{
|
|
std::vector<std::pair<unsigned int, float>> extremities = findExtremities();
|
|
for (unsigned int i=0;i<extremities.size();i++)
|
|
{
|
|
meshExtremity(mesh, splines_circles, profondeur, extremities[i]);
|
|
}
|
|
}
|
|
|
|
template<typename... T>
|
|
void Skeleton3D<T...>::meshExtremity(geo::Mesh& mesh, std::vector<std::vector<geo::Circle<float>>> const& splines_circles, unsigned int profondeur, std::pair<unsigned int,float> extremity) const
|
|
{
|
|
// Get the junction elements
|
|
auto tuple = splines[extremity.first](extremity.second);
|
|
auto C_t = std::get<0>(tuple);
|
|
auto r_t = std::get<1>(tuple);
|
|
|
|
auto S_prime_t = splines[extremity.first].prime(extremity.second);
|
|
auto C_prime_t = std::get<0>(S_prime_t);
|
|
|
|
C_prime_t /= C_prime_t.norm();
|
|
|
|
auto intersection = C_t;
|
|
|
|
if (extremity.second == 0)
|
|
{
|
|
intersection -= r_t * C_prime_t;
|
|
}
|
|
else
|
|
{
|
|
intersection += r_t * C_prime_t;
|
|
}
|
|
|
|
geo::Circle<float> extremCircle;
|
|
if (extremity.second == 0)
|
|
{
|
|
extremCircle = splines_circles[extremity.first][0];
|
|
}
|
|
else
|
|
{
|
|
extremCircle = splines_circles[extremity.first][splines_circles[extremity.first].size()-1];
|
|
}
|
|
|
|
|
|
unsigned int intersectionIndex = mesh.vertices.size();
|
|
|
|
if (profondeur==0)
|
|
{
|
|
mesh.vertices.push_back(intersection);
|
|
for (unsigned int i=0;i<extremCircle.points.size();i++)
|
|
{
|
|
auto ip1 = extremCircle.points[i].first;
|
|
if (i==extremCircle.points.size()-1)
|
|
{
|
|
auto ip2 = extremCircle.points[0].first;
|
|
mesh.faces.push_back(sortedIndexes(ip1,intersectionIndex,ip2,extremity.second));
|
|
}
|
|
else
|
|
{
|
|
auto ip2 = extremCircle.points[i+1].first;
|
|
mesh.faces.push_back(sortedIndexes(ip1,intersectionIndex,ip2,extremity.second));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
subdivision(mesh, profondeur, extremCircle, intersectionIndex, intersection, C_t, r_t, extremity.second);
|
|
}
|
|
}
|
|
|
|
template<typename... T>
|
|
void Skeleton3D<T...>::subdivision(geo::Mesh& mesh, unsigned int profondeur, geo::Circle<float> extremCircle, unsigned int intersectionIndex, geo::Vector3<float> intersection, geo::Vector3<float> center, float radius, float t) const
|
|
{
|
|
std::vector<std::vector<geo::Vector3<float>>> arcs;
|
|
for (unsigned int i=0;i<extremCircle.points.size();i++)
|
|
{
|
|
// compute the points build by subdivision on one arc (between a point on the extrem
|
|
// circle and the extremity)
|
|
std::vector<geo::Vector3<float>> arc;
|
|
arc.push_back(intersection);
|
|
arcSubdivise(intersection, extremCircle.points[i].second, profondeur, arc, center, radius);
|
|
arc.push_back(extremCircle.points[i].second);
|
|
arcs.push_back(arc);
|
|
}
|
|
|
|
// Compute the mesh
|
|
|
|
//push the new vertices
|
|
unsigned int indexVertices = mesh.vertices.size();
|
|
unsigned int indexVerticesInit = indexVertices;
|
|
unsigned int nbPoints = arcs[0].size()-2;
|
|
|
|
//push the intersection
|
|
mesh.vertices.push_back(arcs[0][0]);
|
|
|
|
// push all the new vertices
|
|
for (unsigned int i=0;i<arcs.size();i++)
|
|
{
|
|
for (unsigned int j=1;j<arcs[i].size()-1;j++)
|
|
{
|
|
mesh.vertices.push_back(arcs[i][j]);
|
|
}
|
|
}
|
|
|
|
//push the faces
|
|
for (unsigned int i=0;i<arcs.size()-1;i++)
|
|
{
|
|
auto courant = arcs[i];
|
|
|
|
for (unsigned int j=0;j<courant.size()-2;j++)
|
|
{
|
|
if (j==0)
|
|
{
|
|
mesh.faces.push_back(sortedIndexes(intersectionIndex, indexVertices + 1 + nbPoints, indexVertices + 1,t));
|
|
}
|
|
else
|
|
{
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + j, indexVertices + j + 1 + nbPoints, indexVertices + j + 1, t));
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + j, indexVertices + j + nbPoints, indexVertices + j + 1 + nbPoints, t));
|
|
}
|
|
}
|
|
|
|
// before last point
|
|
unsigned int i1 = extremCircle.points[i].first;
|
|
unsigned int i2 = extremCircle.points[i+1].first;
|
|
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + nbPoints, i2, i1, t));
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + nbPoints , indexVertices + 2*nbPoints , i2, t));
|
|
|
|
// increment the indexVertices
|
|
indexVertices += nbPoints;
|
|
|
|
}
|
|
|
|
// the last mesh (last arc)
|
|
auto courant = arcs[arcs.size()-1];
|
|
|
|
for (unsigned int j=0;j<courant.size()-2;j++)
|
|
{
|
|
if (j==0)
|
|
{
|
|
mesh.faces.push_back(sortedIndexes(intersectionIndex, indexVerticesInit + 1, indexVertices + 1, t));
|
|
}
|
|
else
|
|
{
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + j, indexVerticesInit + j + 1, indexVertices + j + 1, t));
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + j, indexVerticesInit + j, indexVerticesInit + j + 1 , t));
|
|
}
|
|
}
|
|
|
|
// before last point
|
|
unsigned int i1 = extremCircle.points[arcs.size()-1].first;
|
|
unsigned int i2 = extremCircle.points[0].first;
|
|
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + nbPoints, i2, i1, t));
|
|
mesh.faces.push_back(sortedIndexes(indexVertices + nbPoints, indexVerticesInit + nbPoints, i2, t));
|
|
}
|
|
|
|
template<typename... T>
|
|
geo::Vector3<unsigned int> Skeleton3D<T... >::sortedIndexes(unsigned int a, unsigned int b, unsigned int c, float t) const
|
|
{
|
|
if(t==0)
|
|
{
|
|
return geo::Vector3<unsigned int> {a,b,c};
|
|
}
|
|
else
|
|
{
|
|
return geo::Vector3<unsigned int> {a,c,b};
|
|
}
|
|
}
|
|
|
|
//pt1 the extremity
|
|
template<typename... T>
|
|
void Skeleton3D<T...>::arcSubdivise(geo::Vector3<float> pt1, geo::Vector3<float> pt2, unsigned int profondeur, std::vector<geo::Vector3<float>>& arc, geo::Vector3<float> center, float radius) const
|
|
{
|
|
if (profondeur!=0)
|
|
{
|
|
// compute middle of arc
|
|
geo::Vector3<float> middleSegment;
|
|
middleSegment = (pt1+pt2)/2;
|
|
// projection on the sphere
|
|
geo::Vector3<float> direction = (middleSegment - center);
|
|
direction /= direction.norm();
|
|
direction *= radius;
|
|
geo::Vector3<float> middle = center + direction;
|
|
|
|
arcSubdivise(pt1, middle, profondeur-1, arc, center, radius);
|
|
arc.push_back(middle);
|
|
arcSubdivise(middle, pt2, profondeur-1, arc, center, radius);
|
|
}
|
|
}
|
|
|
|
template<typename... T>
|
|
geo::Mesh Skeleton3D<T...>::computeMesh(float pointsProportion, unsigned int nbPointsPerCircle, unsigned int depth,bool withExtremities, bool withJunctions) const
|
|
{
|
|
geo::Mesh mesh;
|
|
std::vector<std::vector<geo::Circle<float>>> splines_circles;
|
|
unsigned int globalOffset = 0;
|
|
|
|
for (auto const& spline : splines)
|
|
{
|
|
float l = geo::splineLenght(spline);
|
|
unsigned int nbCirclesOnTheSpline = std::max(static_cast<unsigned int>(std::round(pointsProportion*l)),2u);
|
|
auto spl = spline.computeCircles(nbCirclesOnTheSpline,nbPointsPerCircle,globalOffset);
|
|
splines_circles.push_back(spl);
|
|
mesh += geo::splineMeshing(spl);
|
|
globalOffset+=nbPointsPerCircle*spl.size();
|
|
}
|
|
|
|
if (withExtremities)
|
|
meshExtremities(mesh, splines_circles, depth);
|
|
|
|
if (withJunctions)
|
|
meshJunctions(mesh, splines_circles);
|
|
|
|
return mesh;
|
|
}
|
|
|
|
template<typename... T>
|
|
std::ostream& operator<<(std::ostream& out, Skeleton3D<T...> const& s)
|
|
{
|
|
for (auto const& spline:s.splines)
|
|
{
|
|
out << spline << std::endl;
|
|
}
|
|
|
|
for (auto const& junction:s.junctions)
|
|
{
|
|
out << "l " << junction.size() << " ";
|
|
for (auto const& pair:junction)
|
|
{
|
|
out << pair.first << " ";
|
|
}
|
|
for (auto const& pair:junction)
|
|
{
|
|
out << pair.second << " ";
|
|
}
|
|
out << std::endl;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
namespace detail
|
|
{
|
|
|
|
template<typename T>
|
|
bool whichSide(geo::Vector<float,4> const& plane, geo::Vector3<float> const& point)
|
|
{
|
|
return plane.x()*point.x() + plane.y()*point.y() + plane.z()*point.z() < - plane.t();
|
|
}
|
|
|
|
template<typename T>
|
|
geo::Vector<float,4> closestPlane(std::vector<geo::Vector3<T>> const& points)
|
|
{
|
|
// for 3 points we just compute the plane that contains the 3 points
|
|
if (points.size() == 3)
|
|
{
|
|
auto vec = crossProduct(points[1] - points[0], points[2] - points[0]);
|
|
vec /= vec.norm();
|
|
float t = - (points[0].x()*vec.x() + points[0].y()*vec.y() + points[0].z()*vec.z());
|
|
return geo::Vector<float,4>{vec.x(), vec.y(), vec.z(), t};
|
|
}
|
|
// for more points we use an PCA decomposition to find the best plane
|
|
else
|
|
{
|
|
Eigen::MatrixXf m{points.size(),3};
|
|
|
|
for ( unsigned int i = 0; i < points.size(); i++)
|
|
{
|
|
m(i,0) = points[i].x();
|
|
m(i,1) = points[i].y();
|
|
m(i,2) = points[i].z();
|
|
}
|
|
|
|
Eigen::MatrixXf centered = m.rowwise() - m.colwise().mean();
|
|
Eigen::MatrixXf cov = centered.adjoint() * centered;
|
|
|
|
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig{cov};
|
|
|
|
Eigen::Vector3f v1 = eig.eigenvectors().col(1);
|
|
Eigen::Vector3f v2 = eig.eigenvectors().col(2);
|
|
|
|
Eigen::Vector3f n = v1.cross(v2);
|
|
|
|
return( geo::Vector<float,4>{n(0),n(1),n(2), -n.dot(m.colwise().mean())} );
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
std::pair<std::vector<std::list<geo::Point<T>>>, std::vector<std::list<geo::Point<T>>>> sortedPoints(geo::Vector<T,4> const& plane, std::vector<geo::Circle<T>> const& junction_circles)
|
|
{
|
|
std::vector<std::list<geo::Point<float>>> up_points;
|
|
std::vector<std::list<geo::Point<float>>> down_points;
|
|
|
|
for (unsigned int i = 0; i < junction_circles.size(); i++)
|
|
{
|
|
std::list<geo::Point<float>> first_points; // points located on one side of the circle, the first side reached.The two sides are separated by the median plane computed earlier.
|
|
std::list<geo::Point<float>> second_points; // points located on the other side of the circle
|
|
const bool up_side = whichSide<double>(plane, junction_circles[i].points[0].second); // constant boolean initialized at the beginning to know with which side we begin the way on the circle.
|
|
bool side_current_points = up_side; // boolean to know on which side we must had the points.
|
|
|
|
for (unsigned int j = 0; j < junction_circles[i].points.size(); j++)
|
|
{
|
|
if (up_side == side_current_points)
|
|
{
|
|
if (whichSide<double>(plane, junction_circles[i].points[j].second) == up_side)
|
|
{
|
|
first_points.push_back(junction_circles[i].points[j]);
|
|
}
|
|
else
|
|
{
|
|
side_current_points = whichSide<double>(plane, junction_circles[i].points[j].second);
|
|
second_points.push_back(junction_circles[i].points[j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (whichSide<double>(plane, junction_circles[i].points[j].second) == side_current_points)
|
|
{
|
|
second_points.push_back(junction_circles[i].points[j]);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for (int j = static_cast<int>(junction_circles[i].points.size()-1); j >= 0; j--)
|
|
{
|
|
if (whichSide<double>(plane, junction_circles[i].points[j].second) == up_side)
|
|
{
|
|
first_points.push_front(junction_circles[i].points[j]);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (up_side)
|
|
{
|
|
up_points.push_back(first_points);
|
|
down_points.push_back(second_points);
|
|
}
|
|
else
|
|
{
|
|
up_points.push_back(second_points);
|
|
down_points.push_back(first_points);
|
|
|
|
}
|
|
}
|
|
|
|
return std::make_pair(up_points, down_points);
|
|
}
|
|
|
|
template<typename T>
|
|
void pushFaceOriented(geo::Mesh& mesh, geo::Vector3<unsigned int> const& face, geo::Vector3<T> const& sphere_center)
|
|
{
|
|
geo::Vector3<float> v1 = mesh.vertices[face.x()] - sphere_center;
|
|
geo::Vector3<float> v2 = geo::crossProduct(mesh.vertices[face.z()] - mesh.vertices[face.y()],
|
|
mesh.vertices[face.z()] - mesh.vertices[face.x()]);
|
|
|
|
if (v1*v2 < 0)
|
|
{
|
|
mesh.faces.push_back(face);
|
|
}
|
|
else
|
|
{
|
|
mesh.faces.push_back(geo::Vector3<unsigned int>{face.x(),face.z(),face.y()});
|
|
}
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
} // namespace pae
|