// This file is part of libigl, a simple c++ geometry processing library. // // Copyright (C) 2018 Zhongshi Jiang // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #include "exact_geodesic.h" //Copyright (C) 2008 Danil Kirsanov, MIT License //Code from https://code.google.com/archive/p/geodesic/ // Compiled into a single file by Zhongshi Jiang #include #include #include #include #include #include #include #include #include #include #include namespace igl{ namespace geodesic{ //#include "geodesic_constants_and_simple_functions.h" //double const GEODESIC_INF = std::numeric_limits::max(); double const GEODESIC_INF = 1e100; //in order to avoid numerical problems with "infinitely small" intervals, //we drop all the intervals smaller than SMALLEST_INTERVAL_RATIO*edge_length double const SMALLEST_INTERVAL_RATIO = 1e-6; //double const SMALL_EPSILON = 1e-10; inline double cos_from_edges(double const a, //compute the cosine of the angle given the lengths of the edges double const b, double const c) { assert(a>1e-50); assert(b>1e-50); assert(c>1e-50); double result = (b*b + c*c - a*a)/(2.0*b*c); result = std::max(result, -1.0); return std::min(result, 1.0); } inline double angle_from_edges(double const a, //compute the cosine of the angle given the lengths of the edges double const b, double const c) { return acos(cos_from_edges(a,b,c)); } template inline bool read_mesh_from_file(char* filename, Points& points, Faces& faces) { std::ifstream file(filename); assert(file.is_open()); if(!file.is_open()) return false; unsigned num_points; file >> num_points; assert(num_points>=3); unsigned num_faces; file >> num_faces; points.resize(num_points*3); for(typename Points::iterator i=points.begin(); i!=points.end(); ++i) { file >> *i; } faces.resize(num_faces*3); for(typename Faces::iterator i=faces.begin(); i!=faces.end(); ++i) { file >> *i; } file.close(); return true; } // #include "geodesic_memory" template //quickly allocates multiple elements of a given type; no deallocation class SimlpeMemoryAllocator { public: typedef T* pointer; SimlpeMemoryAllocator(unsigned block_size = 0, unsigned max_number_of_blocks = 0) { reset(block_size, max_number_of_blocks); }; ~SimlpeMemoryAllocator(){}; void reset(unsigned block_size, unsigned max_number_of_blocks) { m_block_size = block_size; m_max_number_of_blocks = max_number_of_blocks; m_current_position = 0; m_storage.reserve(max_number_of_blocks); m_storage.resize(1); m_storage[0].resize(block_size); }; pointer allocate(unsigned const n) //allocate n units { assert(n < m_block_size); if(m_current_position + n >= m_block_size) { m_storage.push_back( std::vector() ); m_storage.back().resize(m_block_size); m_current_position = 0; } pointer result = & m_storage.back()[m_current_position]; m_current_position += n; return result; }; private: std::vector > m_storage; unsigned m_block_size; //size of a single block unsigned m_max_number_of_blocks; //maximum allowed number of blocks unsigned m_current_position; //first unused element inside the current block }; template //quickly allocates and deallocates single elements of a given type class MemoryAllocator { public: typedef T* pointer; MemoryAllocator(unsigned block_size = 1024, unsigned max_number_of_blocks = 1024) { reset(block_size, max_number_of_blocks); }; ~MemoryAllocator(){}; void clear() { reset(m_block_size, m_max_number_of_blocks); } void reset(unsigned block_size, unsigned max_number_of_blocks) { m_block_size = block_size; m_max_number_of_blocks = max_number_of_blocks; assert(m_block_size > 0); assert(m_max_number_of_blocks > 0); m_current_position = 0; m_storage.reserve(max_number_of_blocks); m_storage.resize(1); m_storage[0].resize(block_size); m_deleted.clear(); m_deleted.reserve(2*block_size); }; pointer allocate() //allocates single unit of memory { pointer result; if(m_deleted.empty()) { if(m_current_position + 1 >= m_block_size) { m_storage.push_back( std::vector() ); m_storage.back().resize(m_block_size); m_current_position = 0; } result = & m_storage.back()[m_current_position]; ++m_current_position; } else { result = m_deleted.back(); m_deleted.pop_back(); } return result; }; void deallocate(pointer p) //allocate n units { if(m_deleted.size() < m_deleted.capacity()) { m_deleted.push_back(p); } }; private: std::vector > m_storage; unsigned m_block_size; //size of a single block unsigned m_max_number_of_blocks; //maximum allowed number of blocks unsigned m_current_position; //first unused element inside the current block std::vector m_deleted; //pointers to deleted elemets }; class OutputBuffer { public: OutputBuffer(): m_num_bytes(0) {} void clear() { m_num_bytes = 0; m_buffer = std::shared_ptr(); } template T* allocate(unsigned n) { double wanted = n*sizeof(T); if(wanted > m_num_bytes) { unsigned new_size = (unsigned) ceil(wanted / (double)sizeof(double)); m_buffer = std::shared_ptr(new double[new_size]); m_num_bytes = new_size*sizeof(double); } return (T*)m_buffer.get(); } template T* get() { return (T*)m_buffer.get(); } template unsigned capacity() { return (unsigned)floor((double)m_num_bytes/(double)sizeof(T)); }; private: std::shared_ptr m_buffer; unsigned m_num_bytes; }; class Vertex; class Edge; class Face; class Mesh; class MeshElementBase; typedef Vertex* vertex_pointer; typedef Edge* edge_pointer; typedef Face* face_pointer; typedef Mesh* mesh_pointer; typedef MeshElementBase* base_pointer; template //simple vector that stores info about mesh references class SimpleVector //for efficiency, it uses an outside memory allocator { public: SimpleVector(): m_size(0), m_begin(NULL) {}; typedef Data* iterator; unsigned size(){return m_size;}; iterator begin(){return m_begin;}; iterator end(){return m_begin + m_size;}; template void set_allocation(DataPointer begin, unsigned size) { assert(begin != NULL || size == 0); m_size = size; m_begin = (iterator)begin; } Data& operator[](unsigned i) { assert(i < m_size); return *(m_begin + i); } void clear() { m_size = 0; m_begin = NULL; } private: unsigned m_size; Data* m_begin; }; enum PointType { VERTEX, EDGE, FACE, UNDEFINED_POINT }; class MeshElementBase //prototype of vertices, edges and faces { public: typedef SimpleVector vertex_pointer_vector; typedef SimpleVector edge_pointer_vector; typedef SimpleVector face_pointer_vector; MeshElementBase(): m_id(0), m_type(UNDEFINED_POINT) {}; vertex_pointer_vector& adjacent_vertices(){return m_adjacent_vertices;}; edge_pointer_vector& adjacent_edges(){return m_adjacent_edges;}; face_pointer_vector& adjacent_faces(){return m_adjacent_faces;}; unsigned& id(){return m_id;}; PointType type(){return m_type;}; protected: vertex_pointer_vector m_adjacent_vertices; //list of the adjacent vertices edge_pointer_vector m_adjacent_edges; //list of the adjacent edges face_pointer_vector m_adjacent_faces; //list of the adjacent faces unsigned m_id; //unique id PointType m_type; //vertex, edge or face }; class Point3D //point in 3D and corresponding operations { public: Point3D(){}; Point3D(Point3D* p) { x() = p->x(); y() = p->y(); z() = p->z(); }; double* xyz(){return m_coordinates;}; double& x(){return *m_coordinates;}; double& y(){return *(m_coordinates+1);}; double& z(){return *(m_coordinates+2);}; void set(double new_x, double new_y, double new_z) { x() = new_x; y() = new_y; z() = new_z; } void set(double* data) { x() = *data; y() = *(data+1); z() = *(data+2); } double distance(double* v) { double dx = m_coordinates[0] - v[0]; double dy = m_coordinates[1] - v[1]; double dz = m_coordinates[2] - v[2]; return sqrt(dx*dx + dy*dy + dz*dz); }; double distance(Point3D* v) { return distance(v->xyz()); }; void add(Point3D* v) { x() += v->x(); y() += v->y(); z() += v->z(); }; void multiply(double v) { x() *= v; y() *= v; z() *= v; }; private: double m_coordinates[3]; //xyz }; class Vertex: public MeshElementBase, public Point3D { public: Vertex() { m_type = VERTEX; }; ~Vertex(){}; bool& saddle_or_boundary(){return m_saddle_or_boundary;}; private: //this flag speeds up exact geodesic algorithm bool m_saddle_or_boundary; //it is true if total adjacent angle is larger than 2*PI or this vertex belongs to the mesh boundary }; class Face: public MeshElementBase { public: Face() { m_type = FACE; }; ~Face(){}; edge_pointer opposite_edge(vertex_pointer v); vertex_pointer opposite_vertex(edge_pointer e); edge_pointer next_edge(edge_pointer e, vertex_pointer v); double vertex_angle(vertex_pointer v) { for(unsigned i=0; i<3; ++i) { if(adjacent_vertices()[i]->id() == v->id()) { return m_corner_angles[i]; } } assert(0); return 0; } double* corner_angles(){return m_corner_angles;}; private: double m_corner_angles[3]; //triangle angles in radians; angles correspond to vertices in m_adjacent_vertices }; class Edge: public MeshElementBase { public: Edge() { m_type = EDGE; }; ~Edge(){}; double& length(){return m_length;}; face_pointer opposite_face(face_pointer f) { if(adjacent_faces().size() == 1) { assert(adjacent_faces()[0]->id() == f->id()); return NULL; } assert(adjacent_faces()[0]->id() == f->id() || adjacent_faces()[1]->id() == f->id()); return adjacent_faces()[0]->id() == f->id() ? adjacent_faces()[1] : adjacent_faces()[0]; }; vertex_pointer opposite_vertex(vertex_pointer v) { assert(belongs(v)); return adjacent_vertices()[0]->id() == v->id() ? adjacent_vertices()[1] : adjacent_vertices()[0]; }; bool belongs(vertex_pointer v) { return adjacent_vertices()[0]->id() == v->id() || adjacent_vertices()[1]->id() == v->id(); } bool is_boundary(){return adjacent_faces().size() == 1;}; vertex_pointer v0(){return adjacent_vertices()[0];}; vertex_pointer v1(){return adjacent_vertices()[1];}; void local_coordinates(Point3D* point, double& x, double& y) { double d0 = point->distance(v0()); if(d0 < 1e-50) { x = 0.0; y = 0.0; return; } double d1 = point->distance(v1()); if(d1 < 1e-50) { x = m_length; y = 0.0; return; } x = m_length/2.0 + (d0*d0 - d1*d1)/(2.0*m_length); y = sqrt(std::max(0.0, d0*d0 - x*x)); return; } private: double m_length; //length of the edge }; class SurfacePoint:public Point3D //point on the surface of the mesh { public: SurfacePoint(): m_p(NULL) {}; SurfacePoint(vertex_pointer v): //set the surface point in the vertex SurfacePoint::Point3D(v), m_p(v) {}; SurfacePoint(face_pointer f): //set the surface point in the center of the face m_p(f) { set(0,0,0); add(f->adjacent_vertices()[0]); add(f->adjacent_vertices()[1]); add(f->adjacent_vertices()[2]); multiply(1./3.); }; SurfacePoint(edge_pointer e, //set the surface point in the middle of the edge double a = 0.5): m_p(e) { double b = 1 - a; vertex_pointer v0 = e->adjacent_vertices()[0]; vertex_pointer v1 = e->adjacent_vertices()[1]; x() = b*v0->x() + a*v1->x(); y() = b*v0->y() + a*v1->y(); z() = b*v0->z() + a*v1->z(); }; SurfacePoint(base_pointer g, double x, double y, double z, PointType t = UNDEFINED_POINT): m_p(g) { set(x,y,z); }; void initialize(SurfacePoint const& p) { *this = p; } ~SurfacePoint(){}; PointType type(){return m_p ? m_p->type() : UNDEFINED_POINT;}; base_pointer& base_element(){return m_p;}; protected: base_pointer m_p; //could be face, vertex or edge pointer }; inline edge_pointer Face::opposite_edge(vertex_pointer v) { for(unsigned i=0; i<3; ++i) { edge_pointer e = adjacent_edges()[i]; if(!e->belongs(v)) { return e; } } assert(0); return NULL; } inline vertex_pointer Face::opposite_vertex(edge_pointer e) { for(unsigned i=0; i<3; ++i) { vertex_pointer v = adjacent_vertices()[i]; if(!e->belongs(v)) { return v; } } assert(0); return NULL; } inline edge_pointer Face::next_edge(edge_pointer e, vertex_pointer v) { assert(e->belongs(v)); for(unsigned i=0; i<3; ++i) { edge_pointer next = adjacent_edges()[i]; if(e->id() != next->id() && next->belongs(v)) { return next; } } assert(0); return NULL; } struct HalfEdge //prototype of the edge; used for mesh construction { unsigned face_id; unsigned vertex_0; //adjacent vertices sorted by id value unsigned vertex_1; //they are sorted, vertex_0 < vertex_1 }; inline bool operator < (const HalfEdge &x, const HalfEdge &y) { if(x.vertex_0 == y.vertex_0) { return x.vertex_1 < y.vertex_1; } else { return x.vertex_0 < y.vertex_0; } } inline bool operator != (const HalfEdge &x, const HalfEdge &y) { return x.vertex_0 != y.vertex_0 || x.vertex_1 != y.vertex_1; } inline bool operator == (const HalfEdge &x, const HalfEdge &y) { return x.vertex_0 == y.vertex_0 && x.vertex_1 == y.vertex_1; } struct edge_visible_from_source { unsigned source; edge_pointer edge; }; class Mesh { public: Mesh() {}; ~Mesh(){}; template void initialize_mesh_data(unsigned num_vertices, Points& p, unsigned num_faces, Faces& tri); //build mesh from regular point-triangle representation template void initialize_mesh_data(Points& p, Faces& tri); //build mesh from regular point-triangle representation std::vector& vertices(){return m_vertices;}; std::vector& edges(){return m_edges;}; std::vector& faces(){return m_faces;}; unsigned closest_vertices(SurfacePoint* p, std::vector* storage = NULL); //list vertices closest to the point private: void build_adjacencies(); //build internal structure of the mesh bool verify(); //verifies connectivity of the mesh and prints some debug info typedef void* void_pointer; void_pointer allocate_pointers(unsigned n) { return m_pointer_allocator.allocate(n); } std::vector m_vertices; std::vector m_edges; std::vector m_faces; SimlpeMemoryAllocator m_pointer_allocator; //fast memory allocating for Face/Vertex/Edge cross-references }; inline unsigned Mesh::closest_vertices(SurfacePoint* p, std::vector* storage) { assert(p->type() != UNDEFINED_POINT); if(p->type() == VERTEX) { if(storage) { storage->push_back(static_cast(p->base_element())); } return 1; } else if(p->type() == FACE) { if(storage) { vertex_pointer* vp= p->base_element()->adjacent_vertices().begin(); storage->push_back(*vp); storage->push_back(*(vp+1)); storage->push_back(*(vp+2)); } return 2; } else if(p->type() == EDGE) //for edge include all 4 adjacent vertices { edge_pointer edge = static_cast(p->base_element()); if(storage) { storage->push_back(edge->adjacent_vertices()[0]); storage->push_back(edge->adjacent_vertices()[1]); for(unsigned i = 0; i < edge->adjacent_faces().size(); ++i) { face_pointer face = edge->adjacent_faces()[i]; storage->push_back(face->opposite_vertex(edge)); } } return 2 + edge->adjacent_faces().size(); } assert(0); return 0; } template void Mesh::initialize_mesh_data(Points& p, Faces& tri) //build mesh from regular point-triangle representation { assert(p.size() % 3 == 0); unsigned const num_vertices = p.size() / 3; assert(tri.size() % 3 == 0); unsigned const num_faces = tri.size() / 3; initialize_mesh_data(num_vertices, p, num_faces, tri); } template void Mesh::initialize_mesh_data(unsigned num_vertices, Points& p, unsigned num_faces, Faces& tri) { unsigned const approximate_number_of_internal_pointers = (num_vertices + num_faces)*4; unsigned const max_number_of_pointer_blocks = 100; m_pointer_allocator.reset(approximate_number_of_internal_pointers, max_number_of_pointer_blocks); m_vertices.resize(num_vertices); for(unsigned i=0; iadjacent Faces std::vector count(m_vertices.size()); //count adjacent vertices for(unsigned i=0; iid(); assert(vertex_id < m_vertices.size()); count[vertex_id]++; } } for(unsigned i=0; iadjacent_faces()[count[v->id()]++] = &f; } } //find all edges //i.e. find all half-edges, sort and combine them into edges std::vector half_edges(m_faces.size()*3); unsigned k = 0; for(unsigned i=0; iid(); unsigned vertex_id_2 = f.adjacent_vertices()[(j+1) % 3]->id(); half_edges[k].vertex_0 = std::min(vertex_id_1, vertex_id_2); half_edges[k].vertex_1 = std::max(vertex_id_1, vertex_id_2); k++; } } std::sort(half_edges.begin(), half_edges.end()); unsigned number_of_edges = 1; for(unsigned i=1; iadjacent Vertices and Faces m_edges.resize(number_of_edges); unsigned edge_id = 0; for(unsigned i=0; idistance(e.adjacent_vertices()[1]); assert(e.length() > 1e-100); //algorithm works well with non-degenerate meshes only if(i != half_edges.size()-1 && half_edges[i] == half_edges[i+1]) //double edge { e.adjacent_faces().set_allocation(allocate_pointers(2),2); e.adjacent_faces()[0] = &m_faces[half_edges[i].face_id]; e.adjacent_faces()[1] = &m_faces[half_edges[i+1].face_id]; i += 2; } else //single edge { e.adjacent_faces().set_allocation(allocate_pointers(1),1); //one adjucent faces e.adjacent_faces()[0] = &m_faces[half_edges[i].face_id]; i += 1; } } // Vertices->adjacent Edges std::fill(count.begin(), count.end(), 0); for(unsigned i=0; iid()]++; count[e.adjacent_vertices()[1]->id()]++; } for(unsigned i=0; iadjacent_edges()[count[v->id()]++] = &e; } } // Faces->adjacent Edges for(unsigned i=0; iid()]<3); f->adjacent_edges()[count[f->id()]++] = &e; } } //compute angles for the faces for(unsigned i=0; ilength(); } double angle = angle_from_edges(abc[0], abc[1], abc[2]); assert(angle>1e-5); //algorithm works well with non-degenerate meshes only f.corner_angles()[j] = angle; sum += angle; } assert(std::abs(sum - igl::PI) < 1e-5); //algorithm works well with non-degenerate meshes only } //define m_turn_around_flag for vertices std::vector total_vertex_angle(m_vertices.size()); for(unsigned i=0; iid()] += f.corner_angles()[j]; } } for(unsigned i=0; i 2.0*igl::PI - 1e-5); } for(unsigned i=0; isaddle_or_boundary() = true; e.adjacent_vertices()[1]->saddle_or_boundary() = true; } } assert(verify()); } inline bool Mesh::verify() //verifies connectivity of the mesh and prints some debug info { std::cout << std::endl; // make sure that all vertices are mentioned at least once. // though the loose vertex is not a bug, it most likely indicates that something is wrong with the mesh std::vector map(m_vertices.size(), false); for(unsigned i=0; iadjacent_vertices()[0]->id()] = true; map[e->adjacent_vertices()[1]->id()] = true; } assert(std::find(map.begin(), map.end(), false) == map.end()); //make sure that the mesh is connected trough its edges //if mesh has more than one connected component, it is most likely a bug std::vector stack(1,&m_faces[0]); stack.reserve(m_faces.size()); map.resize(m_faces.size()); std::fill(map.begin(), map.end(), false); map[0] = true; while(!stack.empty()) { face_pointer f = stack.back(); stack.pop_back(); for(unsigned i=0; i<3; ++i) { edge_pointer e = f->adjacent_edges()[i]; face_pointer f_adjacent = e->opposite_face(f); if(f_adjacent && !map[f_adjacent->id()]) { map[f_adjacent->id()] = true; stack.push_back(f_adjacent); } } } assert(std::find(map.begin(), map.end(), false) == map.end()); //print some mesh statistics that can be useful in debugging // std::cout << "mesh has " << m_vertices.size() // << " vertices, " << m_faces.size() // << " faces, " << m_edges.size() // << " edges\n"; unsigned total_boundary_edges = 0; double longest_edge = 0; double shortest_edge = 1e100; for(unsigned i=0; iset(data); unsigned type = (unsigned) data[3]; unsigned id = (unsigned) data[4]; if(type == 0) //vertex { point->base_element() = &mesh->vertices()[id]; } else if(type == 1) //edge { point->base_element() = &mesh->edges()[id]; } else //face { point->base_element() = &mesh->faces()[id]; } } inline void fill_surface_point_double(geodesic::SurfacePoint* point, double* data, long mesh_id) { data[0] = point->x(); data[1] = point->y(); data[2] = point->z(); data[4] = point->base_element()->id(); if(point->type() == VERTEX) //vertex { data[3] = 0; } else if(point->type() == EDGE) //edge { data[3] = 1; } else //face { data[3] = 2; } } class Interval; class IntervalList; typedef Interval* interval_pointer; typedef IntervalList* list_pointer; class Interval //interval of the edge { public: Interval(){}; ~Interval(){}; enum DirectionType { FROM_FACE_0, FROM_FACE_1, FROM_SOURCE, UNDEFINED_DIRECTION }; double signal(double x) //geodesic distance function at point x { assert(x>=0.0 && x <= m_edge->length()); if(m_d == GEODESIC_INF) { return GEODESIC_INF; } else { double dx = x - m_pseudo_x; if(m_pseudo_y == 0.0) { return m_d + std::abs(dx); } else { return m_d + sqrt(dx*dx + m_pseudo_y*m_pseudo_y); } } } double max_distance(double end) { if(m_d == GEODESIC_INF) { return GEODESIC_INF; } else { double a = std::abs(m_start - m_pseudo_x); double b = std::abs(end - m_pseudo_x); return a > b ? m_d + sqrt(a*a + m_pseudo_y*m_pseudo_y): m_d + sqrt(b*b + m_pseudo_y*m_pseudo_y); } } void compute_min_distance(double stop) //compute min, given c,d theta, start, end. { assert(stop > m_start); if(m_d == GEODESIC_INF) { m_min = GEODESIC_INF; } else if(m_start > m_pseudo_x) { m_min = signal(m_start); } else if(stop < m_pseudo_x) { m_min = signal(stop); } else { assert(m_pseudo_y<=0); m_min = m_d - m_pseudo_y; } } //compare two intervals in the queue bool operator()(interval_pointer const x, interval_pointer const y) const { if(x->min() != y->min()) { return x->min() < y->min(); } else if(x->start() != y->start()) { return x->start() < y->start(); } else { return x->edge()->id() < y->edge()->id(); } } double stop() //return the endpoint of the interval { return m_next ? m_next->start() : m_edge->length(); } double hypotenuse(double a, double b) { return sqrt(a*a + b*b); } void find_closest_point(double const x, double const y, double& offset, double& distance); //find the point on the interval that is closest to the point (alpha, s) double& start(){return m_start;}; double& d(){return m_d;}; double& pseudo_x(){return m_pseudo_x;}; double& pseudo_y(){return m_pseudo_y;}; double& min(){return m_min;}; interval_pointer& next(){return m_next;}; edge_pointer& edge(){return m_edge;}; DirectionType& direction(){return m_direction;}; bool visible_from_source(){return m_direction == FROM_SOURCE;}; unsigned& source_index(){return m_source_index;}; void initialize(edge_pointer edge, SurfacePoint* point = NULL, unsigned source_index = 0); protected: double m_start; //initial point of the interval on the edge double m_d; //distance from the source to the pseudo-source double m_pseudo_x; //coordinates of the pseudo-source in the local coordinate system double m_pseudo_y; //y-coordinate should be always negative double m_min; //minimum distance on the interval interval_pointer m_next; //pointer to the next interval in the list edge_pointer m_edge; //edge that the interval belongs to unsigned m_source_index; //the source it belongs to DirectionType m_direction; //where the interval is coming from }; struct IntervalWithStop : public Interval { public: double& stop(){return m_stop;}; protected: double m_stop; }; class IntervalList //list of the of intervals of the given edge { public: IntervalList(){m_first = NULL;}; ~IntervalList(){}; void clear() { m_first = NULL; }; void initialize(edge_pointer e) { m_edge = e; m_first = NULL; }; interval_pointer covering_interval(double offset) //returns the interval that covers the offset { assert(offset >= 0.0 && offset <= m_edge->length()); interval_pointer p = m_first; while(p && p->stop() < offset) { p = p->next(); } return p;// && p->start() <= offset ? p : NULL; }; void find_closest_point(SurfacePoint* point, double& offset, double& distance, interval_pointer& interval) { interval_pointer p = m_first; distance = GEODESIC_INF; interval = NULL; double x,y; m_edge->local_coordinates(point, x, y); while(p) { if(p->min()find_closest_point(x, y, o, d); if(d < distance) { distance = d; offset = o; interval = p; } } p = p->next(); } }; unsigned number_of_intervals() { interval_pointer p = m_first; unsigned count = 0; while(p) { ++count; p = p->next(); } return count; } interval_pointer last() { interval_pointer p = m_first; if(p) { while(p->next()) { p = p->next(); } } return p; } double signal(double x) { interval_pointer interval = covering_interval(x); return interval ? interval->signal(x) : GEODESIC_INF; } interval_pointer& first(){return m_first;}; edge_pointer& edge(){return m_edge;}; private: interval_pointer m_first; //pointer to the first member of the list edge_pointer m_edge; //edge that owns this list }; class SurfacePointWithIndex : public SurfacePoint { public: unsigned index(){return m_index;}; void initialize(SurfacePoint& p, unsigned index) { SurfacePoint::initialize(p); m_index = index; } bool operator()(SurfacePointWithIndex* x, SurfacePointWithIndex* y) const //used for sorting { assert(x->type() != UNDEFINED_POINT && y->type() !=UNDEFINED_POINT); if(x->type() != y->type()) { return x->type() < y->type(); } else { return x->base_element()->id() < y->base_element()->id(); } } private: unsigned m_index; }; class SortedSources : public std::vector { private: typedef std::vector sorted_vector_type; public: typedef sorted_vector_type::iterator sorted_iterator; typedef std::pair sorted_iterator_pair; sorted_iterator_pair sources(base_pointer mesh_element) { m_search_dummy.base_element() = mesh_element; return equal_range(m_sorted.begin(), m_sorted.end(), &m_search_dummy, m_compare_less); } void initialize(std::vector& sources) //we initialize the sources by copie { resize(sources.size()); m_sorted.resize(sources.size()); for(unsigned i=0; ilength(); if(std::abs(hs+hc) < local_epsilon) { if(rs<=m_start) { r = m_start; d_out = signal(m_start) + std::abs(rs - m_start); } else if(rs>=end) { r = end; d_out = signal(end) + fabs(end - rs); } else { r = rs; d_out = signal(rs); } } else { double ri = (rs*hc + hs*rc)/(hs+hc); if(riend) { r = end; d_out = signal(end) + hypotenuse(end - rs, hs); } else { r = ri; d_out = m_d + hypotenuse(rc - rs, hc + hs); } } } inline void Interval::initialize(edge_pointer edge, SurfacePoint* source, unsigned source_index) { m_next = NULL; //m_geodesic_previous = NULL; m_direction = UNDEFINED_DIRECTION; m_edge = edge; m_source_index = source_index; m_start = 0.0; //m_stop = edge->length(); if(!source) { m_d = GEODESIC_INF; m_min = GEODESIC_INF; return; } m_d = 0; if(source->base_element()->type() == VERTEX) { if(source->base_element()->id() == edge->v0()->id()) { m_pseudo_x = 0.0; m_pseudo_y = 0.0; m_min = 0.0; return; } else if(source->base_element()->id() == edge->v1()->id()) { m_pseudo_x = stop(); m_pseudo_y = 0.0; m_min = 0.0; return; } } edge->local_coordinates(source, m_pseudo_x, m_pseudo_y); m_pseudo_y = -m_pseudo_y; compute_min_distance(stop()); } // #include "geodesic_algorithm_base.h" class GeodesicAlgorithmBase { public: enum AlgorithmType { EXACT, DIJKSTRA, SUBDIVISION, UNDEFINED_ALGORITHM }; GeodesicAlgorithmBase(geodesic::Mesh* mesh): m_type(UNDEFINED_ALGORITHM), m_max_propagation_distance(1e100), m_mesh(mesh) {}; virtual ~GeodesicAlgorithmBase(){}; virtual void propagate(std::vector& sources, double max_propagation_distance = GEODESIC_INF, //propagation algorithm stops after reaching the certain distance from the source std::vector* stop_points = NULL) = 0; //or after ensuring that all the stop_points are covered virtual void trace_back(SurfacePoint& destination, //trace back piecewise-linear path std::vector& path) = 0; void geodesic(SurfacePoint& source, SurfacePoint& destination, std::vector& path); //lazy people can find geodesic path with one function call void geodesic(std::vector& sources, std::vector& destinations, std::vector >& paths); //lazy people can find geodesic paths with one function call virtual unsigned best_source(SurfacePoint& point, //after propagation step is done, quickly find what source this point belongs to and what is the distance to this source double& best_source_distance) = 0; virtual void print_statistics() //print info about timing and memory usage in the propagation step of the algorithm { std::cout << "propagation step took " << m_time_consumed << " seconds " << std::endl; }; AlgorithmType type(){return m_type;}; virtual std::string name(); geodesic::Mesh* mesh(){return m_mesh;}; protected: void set_stop_conditions(std::vector* stop_points, double stop_distance); double stop_distance() { return m_max_propagation_distance; } AlgorithmType m_type; // type of the algorithm typedef std::pair stop_vertex_with_distace_type; std::vector m_stop_vertices; // algorithm stops propagation after covering certain vertices double m_max_propagation_distance; // or reaching the certain distance geodesic::Mesh* m_mesh; double m_time_consumed; //how much time does the propagation step takes double m_propagation_distance_stopped; //at what distance (if any) the propagation algorithm stopped }; inline double length(std::vector& path) { double length = 0; if(!path.empty()) { for(unsigned i=0; i& path) { std::cout << "number of the points in the path = " << path.size() << ", length of the path = " << length(path) << std::endl; } inline std::string GeodesicAlgorithmBase::name() { switch(m_type) { case EXACT: return "exact"; case DIJKSTRA: return "dijkstra"; case SUBDIVISION: return "subdivision"; default: case UNDEFINED_ALGORITHM: return "undefined"; } } inline void GeodesicAlgorithmBase::geodesic(SurfacePoint& source, SurfacePoint& destination, std::vector& path) //lazy people can find geodesic path with one function call { std::vector sources(1, source); std::vector stop_points(1, destination); double const max_propagation_distance = GEODESIC_INF; propagate(sources, max_propagation_distance, &stop_points); trace_back(destination, path); } inline void GeodesicAlgorithmBase::geodesic(std::vector& sources, std::vector& destinations, std::vector >& paths) //lazy people can find geodesic paths with one function call { double const max_propagation_distance = GEODESIC_INF; propagate(sources, max_propagation_distance, &destinations); //we use desinations as stop points paths.resize(destinations.size()); for(unsigned i=0; i* stop_points, double stop_distance) { m_max_propagation_distance = stop_distance; if(!stop_points) { m_stop_vertices.clear(); return; } m_stop_vertices.resize(stop_points->size()); std::vector possible_vertices; for(unsigned i = 0; i < stop_points->size(); ++i) { SurfacePoint* point = &(*stop_points)[i]; possible_vertices.clear(); m_mesh->closest_vertices(point, &possible_vertices); vertex_pointer closest_vertex = NULL; double min_distance = 1e100; for(unsigned j = 0; j < possible_vertices.size(); ++j) { double distance = point->distance(possible_vertices[j]); if(distance < min_distance) { min_distance = distance; closest_vertex = possible_vertices[j]; } } assert(closest_vertex); m_stop_vertices[i].first = closest_vertex; m_stop_vertices[i].second = min_distance; } } class GeodesicAlgorithmExact : public GeodesicAlgorithmBase { public: GeodesicAlgorithmExact(geodesic::Mesh* mesh): GeodesicAlgorithmBase(mesh), m_memory_allocator(mesh->edges().size(), mesh->edges().size()), m_edge_interval_lists(mesh->edges().size()) { m_type = EXACT; for(unsigned i=0; iedges()[i]); } }; ~GeodesicAlgorithmExact(){}; void propagate(std::vector& sources, double max_propagation_distance = GEODESIC_INF, //propagation algorithm stops after reaching the certain distance from the source std::vector* stop_points = NULL); //or after ensuring that all the stop_points are covered void trace_back(SurfacePoint& destination, //trace back piecewise-linear path std::vector& path); unsigned best_source(SurfacePoint& point, //quickly find what source this point belongs to and what is the distance to this source double& best_source_distance); void print_statistics(); private: typedef std::set IntervalQueue; void update_list_and_queue(list_pointer list, IntervalWithStop* candidates, //up to two candidates unsigned num_candidates); unsigned compute_propagated_parameters(double pseudo_x, double pseudo_y, double d, //parameters of the interval double start, double end, //start/end of the interval double alpha, //corner angle double L, //length of the new edge bool first_interval, //if it is the first interval on the edge bool last_interval, bool turn_left, bool turn_right, IntervalWithStop* candidates); //if it is the last interval on the edge void construct_propagated_intervals(bool invert, edge_pointer edge, face_pointer face, //constructs iNew from the rest of the data IntervalWithStop* candidates, unsigned& num_candidates, interval_pointer source_interval); double compute_positive_intersection(double start, double pseudo_x, double pseudo_y, double sin_alpha, double cos_alpha); //used in construct_propagated_intervals unsigned intersect_intervals(interval_pointer zero, IntervalWithStop* one); //intersecting two intervals with up to three intervals in the end interval_pointer best_first_interval(SurfacePoint& point, double& best_total_distance, double& best_interval_position, unsigned& best_source_index); bool check_stop_conditions(unsigned& index); void clear() { m_memory_allocator.clear(); m_queue.clear(); for(unsigned i=0; iid()]; }; void set_sources(std::vector& sources) { m_sources.initialize(sources); } void initialize_propagation_data(); void list_edges_visible_from_source(MeshElementBase* p, std::vector& storage); //used in initialization long visible_from_source(SurfacePoint& point); //used in backtracing void best_point_on_the_edge_set(SurfacePoint& point, std::vector const& storage, interval_pointer& best_interval, double& best_total_distance, double& best_interval_position); void possible_traceback_edges(SurfacePoint& point, std::vector& storage); bool erase_from_queue(interval_pointer p); IntervalQueue m_queue; //interval queue MemoryAllocator m_memory_allocator; //quickly allocate and deallocate intervals std::vector m_edge_interval_lists; //every edge has its interval data enum MapType {OLD, NEW}; //used for interval intersection MapType map[5]; double start[6]; interval_pointer i_new[5]; unsigned m_queue_max_size; //used for statistics unsigned m_iterations; //used for statistics SortedSources m_sources; }; inline void GeodesicAlgorithmExact::best_point_on_the_edge_set(SurfacePoint& point, std::vector const& storage, interval_pointer& best_interval, double& best_total_distance, double& best_interval_position) { best_total_distance = 1e100; for(unsigned i=0; ifind_closest_point(&point, offset, distance, interval); if(distance < best_total_distance) { best_interval = interval; best_total_distance = distance; best_interval_position = offset; } } } inline void GeodesicAlgorithmExact::possible_traceback_edges(SurfacePoint& point, std::vector& storage) { storage.clear(); if(point.type() == VERTEX) { vertex_pointer v = static_cast(point.base_element()); for(unsigned i=0; iadjacent_faces().size(); ++i) { face_pointer f = v->adjacent_faces()[i]; storage.push_back(f->opposite_edge(v)); } } else if(point.type() == EDGE) { edge_pointer e = static_cast(point.base_element()); for(unsigned i=0; iadjacent_faces().size(); ++i) { face_pointer f = e->adjacent_faces()[i]; storage.push_back(f->next_edge(e,e->v0())); storage.push_back(f->next_edge(e,e->v1())); } } else { face_pointer f = static_cast(point.base_element()); storage.push_back(f->adjacent_edges()[0]); storage.push_back(f->adjacent_edges()[1]); storage.push_back(f->adjacent_edges()[2]); } } inline long GeodesicAlgorithmExact::visible_from_source(SurfacePoint& point) //negative if not visible { assert(point.type() != UNDEFINED_POINT); if(point.type() == EDGE) { edge_pointer e = static_cast(point.base_element()); list_pointer list = interval_list(e); double position = std::min(point.distance(e->v0()), e->length()); interval_pointer interval = list->covering_interval(position); //assert(interval); if(interval && interval->visible_from_source()) { return (long)interval->source_index(); } else { return -1; } } else if(point.type() == FACE) { return -1; } else if(point.type() == VERTEX) { vertex_pointer v = static_cast(point.base_element()); for(unsigned i=0; iadjacent_edges().size(); ++i) { edge_pointer e = v->adjacent_edges()[i]; list_pointer list = interval_list(e); double position = e->v0()->id() == v->id() ? 0.0 : e->length(); interval_pointer interval = list->covering_interval(position); if(interval && interval->visible_from_source()) { return (long)interval->source_index(); } } return -1; } assert(0); return 0; } inline double GeodesicAlgorithmExact::compute_positive_intersection(double start, double pseudo_x, double pseudo_y, double sin_alpha, double cos_alpha) { assert(pseudo_y < 0); double denominator = sin_alpha*(pseudo_x - start) - cos_alpha*pseudo_y; if(denominator<0.0) { return -1.0; } double numerator = -pseudo_y*start; if(numerator < 1e-30) { return 0.0; } if(denominator < 1e-30) { return -1.0; } return numerator/denominator; } inline void GeodesicAlgorithmExact::list_edges_visible_from_source(MeshElementBase* p, std::vector& storage) { assert(p->type() != UNDEFINED_POINT); if(p->type() == FACE) { face_pointer f = static_cast(p); for(unsigned i=0; i<3; ++i) { storage.push_back(f->adjacent_edges()[i]); } } else if(p->type() == EDGE) { edge_pointer e = static_cast(p); storage.push_back(e); } else //VERTEX { vertex_pointer v = static_cast(p); for(unsigned i=0; iadjacent_edges().size(); ++i) { storage.push_back(v->adjacent_edges()[i]); } } } inline bool GeodesicAlgorithmExact::erase_from_queue(interval_pointer p) { if(p->min() < GEODESIC_INF/10.0)// && p->min >= queue->begin()->first) { assert(m_queue.count(p)<=1); //the set is unique IntervalQueue::iterator it = m_queue.find(p); if(it != m_queue.end()) { m_queue.erase(it); return true; } } return false; } inline unsigned GeodesicAlgorithmExact::intersect_intervals(interval_pointer zero, IntervalWithStop* one) //intersecting two intervals with up to three intervals in the end { assert(zero->edge()->id() == one->edge()->id()); assert(zero->stop() > one->start() && zero->start() < one->stop()); assert(one->min() < GEODESIC_INF/10.0); double const local_epsilon = SMALLEST_INTERVAL_RATIO*one->edge()->length(); unsigned N=0; if(zero->min() > GEODESIC_INF/10.0) { start[0] = zero->start(); if(zero->start() < one->start() - local_epsilon) { map[0] = OLD; start[1] = one->start(); map[1] = NEW; N = 2; } else { map[0] = NEW; N = 1; } if(zero->stop() > one->stop() + local_epsilon) { map[N] = OLD; //"zero" interval start[N++] = one->stop(); } start[N+1] = zero->stop(); return N; } double const local_small_epsilon = 1e-8*one->edge()->length(); double D = zero->d() - one->d(); double x0 = zero->pseudo_x(); double x1 = one->pseudo_x(); double R0 = x0*x0 + zero->pseudo_y()*zero->pseudo_y(); double R1 = x1*x1 + one->pseudo_y()*one->pseudo_y(); double inter[2]; //points of intersection char Ninter=0; //number of the points of the intersection if(std::abs(D)local_small_epsilon) { inter[0] = (R1 - R0)/(2.*denom); //one solution Ninter = 1; } } else { double D2 = D*D; double Q = 0.5*(R1-R0-D2); double X = x0 - x1; double A = X*X - D2; double B = Q*X + D2*x0; double C = Q*Q - D2*R0; if (std::abs(A)local_small_epsilon) { inter[0] = -C/B; //one solution Ninter = 1; } } else { double det = B*B-A*C; if(det>local_small_epsilon*local_small_epsilon) //two roots { det = sqrt(det); if(A>0.0) //make sure that the roots are ordered { inter[0] = (-B - det)/A; inter[1] = (-B + det)/A; } else { inter[0] = (-B + det)/A; inter[1] = (-B - det)/A; } if(inter[1] - inter[0] > local_small_epsilon) { Ninter = 2; } else { Ninter = 1; } } else if(det>=0.0) //single root { inter[0] = -B/A; Ninter = 1; } } } //---------------------------find possible intervals--------------------------------------- double left = std::max(zero->start(), one->start()); //define left and right boundaries of the intersection of the intervals double right = std::min(zero->stop(), one->stop()); double good_start[4]; //points of intersection within the (left, right) limits +"left" + "right" good_start[0] = left; char Ngood_start=1; //number of the points of the intersection for(char i=0; i left + local_epsilon && x < right - local_epsilon) { good_start[Ngood_start++] = x; } } good_start[Ngood_start++] = right; MapType mid_map[3]; for(char i=0; isignal(mid) <= one->signal(mid) ? OLD : NEW; } //-----------------------------------output---------------------------------- N = 0; if(zero->start() < left - local_epsilon) //additional "zero" interval { if(mid_map[0] == OLD) //first interval in the map is already the old one { good_start[0] = zero->start(); } else { map[N] = OLD; //"zero" interval start[N++] = zero->start(); } } for(long i=0;istop() > one->stop() + local_epsilon) { if(N==0 || map[N-1] == NEW) { map[N] = OLD; //"zero" interval start[N++] = one->stop(); } } start[0] = zero->start(); // just to make sure that epsilons do not damage anything //start[N] = zero->stop(); return N; } inline void GeodesicAlgorithmExact::initialize_propagation_data() { clear(); IntervalWithStop candidate; std::vector edges_visible_from_source; for(unsigned i=0; ibase_element(), edges_visible_from_source); for(unsigned j=0; jlength(); candidate.compute_min_distance(candidate.stop()); candidate.direction() = Interval::FROM_SOURCE; update_list_and_queue(interval_list(e), &candidate, 1); } } } inline void GeodesicAlgorithmExact::propagate(std::vector& sources, double max_propagation_distance, //propagation algorithm stops after reaching the certain distance from the source std::vector* stop_points) { set_stop_conditions(stop_points, max_propagation_distance); set_sources(sources); initialize_propagation_data(); clock_t start = clock(); unsigned satisfied_index = 0; m_iterations = 0; //for statistics m_queue_max_size = 0; IntervalWithStop candidates[2]; while(!m_queue.empty()) { m_queue_max_size = std::max(static_cast(m_queue.size()), m_queue_max_size); unsigned const check_period = 10; if(++m_iterations % check_period == 0) //check if we covered all required vertices { if (check_stop_conditions(satisfied_index)) { break; } } interval_pointer min_interval = *m_queue.begin(); m_queue.erase(m_queue.begin()); edge_pointer edge = min_interval->edge(); list_pointer list = interval_list(edge); assert(min_interval->d() < GEODESIC_INF); bool const first_interval = min_interval->start() == 0.0; //bool const last_interval = min_interval->stop() == edge->length(); bool const last_interval = min_interval->next() == NULL; bool const turn_left = edge->v0()->saddle_or_boundary(); bool const turn_right = edge->v1()->saddle_or_boundary(); for(unsigned i=0; iadjacent_faces().size(); ++i) //two possible faces to propagate { if(!edge->is_boundary()) //just in case, always propagate boundary edges { if((i == 0 && min_interval->direction() == Interval::FROM_FACE_0) || (i == 1 && min_interval->direction() == Interval::FROM_FACE_1)) { continue; } } face_pointer face = edge->adjacent_faces()[i]; //if we come from 1, go to 2 edge_pointer next_edge = face->next_edge(edge,edge->v0()); unsigned num_propagated = compute_propagated_parameters(min_interval->pseudo_x(), min_interval->pseudo_y(), min_interval->d(), //parameters of the interval min_interval->start(), min_interval->stop(), //start/end of the interval face->vertex_angle(edge->v0()), //corner angle next_edge->length(), //length of the new edge first_interval, //if it is the first interval on the edge last_interval, turn_left, turn_right, candidates); //if it is the last interval on the edge bool propagate_to_right = true; if(num_propagated) { if(candidates[num_propagated-1].stop() != next_edge->length()) { propagate_to_right = false; } bool const invert = next_edge->v0()->id() != edge->v0()->id(); //if the origins coinside, do not invert intervals construct_propagated_intervals(invert, //do not inverse next_edge, face, candidates, num_propagated, min_interval); update_list_and_queue(interval_list(next_edge), candidates, num_propagated); } if(propagate_to_right) { //propogation to the right edge double length = edge->length(); next_edge = face->next_edge(edge,edge->v1()); num_propagated = compute_propagated_parameters(length - min_interval->pseudo_x(), min_interval->pseudo_y(), min_interval->d(), //parameters of the interval length - min_interval->stop(), length - min_interval->start(), //start/end of the interval face->vertex_angle(edge->v1()), //corner angle next_edge->length(), //length of the new edge last_interval, //if it is the first interval on the edge first_interval, turn_right, turn_left, candidates); //if it is the last interval on the edge if(num_propagated) { bool const invert = next_edge->v0()->id() != edge->v1()->id(); //if the origins coinside, do not invert intervals construct_propagated_intervals(invert, //do not inverse next_edge, face, candidates, num_propagated, min_interval); update_list_and_queue(interval_list(next_edge), candidates, num_propagated); } } } } m_propagation_distance_stopped = m_queue.empty() ? GEODESIC_INF : (*m_queue.begin())->min(); clock_t stop = clock(); m_time_consumed = (static_cast(stop)-static_cast(start))/CLOCKS_PER_SEC; /* for(unsigned i=0; ifirst(); assert(p->start() == 0.0); while(p->next()) { assert(p->stop() == p->next()->start()); assert(p->d() < GEODESIC_INF); p = p->next(); } }*/ } inline bool GeodesicAlgorithmExact::check_stop_conditions(unsigned& index) { double queue_distance = (*m_queue.begin())->min(); if(queue_distance < stop_distance()) { return false; } while(index < m_stop_vertices.size()) { vertex_pointer v = m_stop_vertices[index].first; edge_pointer edge = v->adjacent_edges()[0]; //take any edge double distance = edge->v0()->id() == v->id() ? interval_list(edge)->signal(0.0) : interval_list(edge)->signal(edge->length()); if(queue_distance < distance + m_stop_vertices[index].second) { return false; } ++index; } return true; } inline void GeodesicAlgorithmExact::update_list_and_queue(list_pointer list, IntervalWithStop* candidates, //up to two candidates unsigned num_candidates) { assert(num_candidates <= 2); //assert(list->first() != NULL); edge_pointer edge = list->edge(); double const local_epsilon = SMALLEST_INTERVAL_RATIO * edge->length(); if(list->first() == NULL) { interval_pointer* p = &list->first(); IntervalWithStop* first; IntervalWithStop* second; if(num_candidates == 1) { first = candidates; second = candidates; first->compute_min_distance(first->stop()); } else { if(candidates->start() <= (candidates+1)->start()) { first = candidates; second = candidates+1; } else { first = candidates+1; second = candidates; } assert(first->stop() == second->start()); first->compute_min_distance(first->stop()); second->compute_min_distance(second->stop()); } if(first->start() > 0.0) { *p = m_memory_allocator.allocate(); (*p)->initialize(edge); p = &(*p)->next(); } *p = m_memory_allocator.allocate(); memcpy(*p,first,sizeof(Interval)); m_queue.insert(*p); if(num_candidates == 2) { p = &(*p)->next(); *p = m_memory_allocator.allocate(); memcpy(*p,second,sizeof(Interval)); m_queue.insert(*p); } if(second->stop() < edge->length()) { p = &(*p)->next(); *p = m_memory_allocator.allocate(); (*p)->initialize(edge); (*p)->start() = second->stop(); } else { (*p)->next() = NULL; } return; } bool propagate_flag; for(unsigned i=0; ifirst(); assert(p->start() == 0.0); while(p != NULL && p->stop() - local_epsilon < q->start()) { p = p->next(); } while(p != NULL && p->start() < q->stop() - local_epsilon) //go through all old intervals { unsigned const N = intersect_intervals(p, q); //interset two intervals if(N == 1) { if(map[0]==OLD) //if "p" is always better, we do not need to update anything) { if(previous) //close previous interval and put in into the queue { previous->next() = p; previous->compute_min_distance(p->start()); m_queue.insert(previous); previous = NULL; } p = p->next(); } else if(previous) //extend previous interval to cover everything; remove p { previous->next() = p->next(); erase_from_queue(p); m_memory_allocator.deallocate(p); p = previous->next(); } else //p becomes "previous" { previous = p; interval_pointer next = p->next(); erase_from_queue(p); memcpy(previous,q,sizeof(Interval)); previous->start() = start[0]; previous->next() = next; p = next; } continue; } //update_flag = true; Interval swap(*p); //used for swapping information propagate_flag = erase_from_queue(p); for(unsigned j=1; jnext() = p; previous->compute_min_distance(previous->stop()); m_queue.insert(previous); previous = NULL; } i_new[0] = p; p->next() = i_new[1]; p->start() = start[0]; } else if(previous) //extend previous interval to cover everything; remove p { i_new[0] = previous; previous->next() = i_new[1]; m_memory_allocator.deallocate(p); previous = NULL; } else //p becomes "previous" { i_new[0] = p; memcpy(p,q,sizeof(Interval)); p->next() = i_new[1]; p->start() = start[0]; } assert(!previous); for(unsigned j=1; jnext() = swap.next(); } else { current_interval->next() = i_new[j+1]; } current_interval->start() = start[j]; } for(unsigned j=0; jcompute_min_distance(current_interval->stop()); //compute minimal distance if(map[j]==NEW || (map[j]==OLD && propagate_flag)) { m_queue.insert(current_interval); } } } p = swap.next(); } if(previous) //close previous interval and put in into the queue { previous->compute_min_distance(previous->stop()); m_queue.insert(previous); previous = NULL; } } } inline unsigned GeodesicAlgorithmExact::compute_propagated_parameters(double pseudo_x, double pseudo_y, double d, //parameters of the interval double begin, double end, //start/end of the interval double alpha, //corner angle double L, //length of the new edge bool first_interval, //if it is the first interval on the edge bool last_interval, bool turn_left, bool turn_right, IntervalWithStop* candidates) //if it is the last interval on the edge { assert(pseudo_y<=0.0); assert(dstart() = 0.0; p->stop() = L; p->d() = d - pseudo_x; p->pseudo_x() = 0.0; p->pseudo_y() = 0.0; return 1; } else if(last_interval && pseudo_x >= end) { p->start() = 0.0; p->stop() = L; p->d() = d + pseudo_x-end; p->pseudo_x() = end*cos(alpha); p->pseudo_y() = -end*sin(alpha); return 1; } else if(pseudo_x >= begin && pseudo_x <= end) { p->start() = 0.0; p->stop() = L; p->d() = d; p->pseudo_x() = pseudo_x*cos(alpha); p->pseudo_y() = -pseudo_x*sin(alpha); return 1; } else { return 0; } } double sin_alpha = sin(alpha); double cos_alpha = cos(alpha); //important: for the first_interval, this function returns zero only if the new edge is "visible" from the source //if the new edge can be covered only after turn_over, the value is negative (-1.0) double L1 = compute_positive_intersection(begin, pseudo_x, pseudo_y, sin_alpha, cos_alpha); if(L1 < 0 || L1 >= L) { if(first_interval && turn_left) { p->start() = 0.0; p->stop() = L; p->d() = d + sqrt(pseudo_x*pseudo_x + pseudo_y*pseudo_y); p->pseudo_y() = 0.0; p->pseudo_x() = 0.0; return 1; } else { return 0; } } double L2 = compute_positive_intersection(end, pseudo_x, pseudo_y, sin_alpha, cos_alpha); if(L2 < 0 || L2 >= L) { p->start() = L1; p->stop() = L; p->d() = d; p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y; p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y; return 1; } p->start() = L1; p->stop() = L2; p->d() = d; p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y; p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y; assert(p->pseudo_y() <= 0.0); if(!(last_interval && turn_right)) { return 1; } else { p = candidates + 1; p->start() = L2; p->stop() = L; double dx = pseudo_x - end; p->d() = d + sqrt(dx*dx + pseudo_y*pseudo_y); p->pseudo_x() = end*cos_alpha; p->pseudo_y() = -end*sin_alpha; return 2; } } inline void GeodesicAlgorithmExact::construct_propagated_intervals(bool invert, edge_pointer edge, face_pointer face, //constructs iNew from the rest of the data IntervalWithStop* candidates, unsigned& num_candidates, interval_pointer source_interval) //up to two candidates { double edge_length = edge->length(); double local_epsilon = SMALLEST_INTERVAL_RATIO * edge_length; //kill very small intervals in order to avoid precision problems if(num_candidates == 2) { double start = std::min(candidates->start(), (candidates+1)->start()); double stop = std::max(candidates->stop(), (candidates+1)->stop()); if(candidates->stop()-candidates->start() < local_epsilon) // kill interval 0 { *candidates = *(candidates+1); num_candidates = 1; candidates->start() = start; candidates->stop() = stop; } else if ((candidates+1)->stop() - (candidates+1)->start() < local_epsilon) { num_candidates = 1; candidates->start() = start; candidates->stop() = stop; } } IntervalWithStop* first; IntervalWithStop* second; if(num_candidates == 1) { first = candidates; second = candidates; } else { if(candidates->start() <= (candidates+1)->start()) { first = candidates; second = candidates+1; } else { first = candidates+1; second = candidates; } assert(first->stop() == second->start()); } if(first->start() < local_epsilon) { first->start() = 0.0; } if(edge_length - second->stop() < local_epsilon) { second->stop() = edge_length; } //invert intervals if necessary; fill missing data and set pointers correctly Interval::DirectionType direction = edge->adjacent_faces()[0]->id() == face->id() ? Interval::FROM_FACE_0 : Interval::FROM_FACE_1; if(!invert) //in this case everything is straighforward, we do not have to invert the intervals { for(unsigned i=0; inext() = (i == num_candidates - 1) ? NULL : candidates + i + 1; p->edge() = edge; p->direction() = direction; p->source_index() = source_interval->source_index(); p->min() = 0.0; //it will be changed later on assert(p->start() < p->stop()); } } else //now we have to invert the intervals { for(unsigned i=0; inext() = (i == 0) ? NULL : candidates + i - 1; p->edge() = edge; p->direction() = direction; p->source_index() = source_interval->source_index(); double length = edge_length; p->pseudo_x() = length - p->pseudo_x(); double start = length - p->stop(); p->stop() = length - p->start(); p->start() = start; p->min() = 0; assert(p->start() < p->stop()); assert(p->start() >= 0.0); assert(p->stop() <= edge->length()); } } } inline unsigned GeodesicAlgorithmExact::best_source(SurfacePoint& point, //quickly find what source this point belongs to and what is the distance to this source double& best_source_distance) { double best_interval_position; unsigned best_source_index; best_first_interval(point, best_source_distance, best_interval_position, best_source_index); return best_source_index; } inline interval_pointer GeodesicAlgorithmExact::best_first_interval(SurfacePoint& point, double& best_total_distance, double& best_interval_position, unsigned& best_source_index) { assert(point.type() != UNDEFINED_POINT); interval_pointer best_interval = NULL; best_total_distance = GEODESIC_INF; if(point.type() == EDGE) { edge_pointer e = static_cast(point.base_element()); list_pointer list = interval_list(e); best_interval_position = point.distance(e->v0()); best_interval = list->covering_interval(best_interval_position); if(best_interval) { //assert(best_interval && best_interval->d() < GEODESIC_INF); best_total_distance = best_interval->signal(best_interval_position); best_source_index = best_interval->source_index(); } } else if(point.type() == FACE) { face_pointer f = static_cast(point.base_element()); for(unsigned i=0; i<3; ++i) { edge_pointer e = f->adjacent_edges()[i]; list_pointer list = interval_list(e); double offset; double distance; interval_pointer interval; list->find_closest_point(&point, offset, distance, interval); if(interval && distance < best_total_distance) { best_interval = interval; best_total_distance = distance; best_interval_position = offset; best_source_index = interval->source_index(); } } //check for all sources that might be located inside this face SortedSources::sorted_iterator_pair local_sources = m_sources.sources(f); for(SortedSources::sorted_iterator it=local_sources.first; it != local_sources.second; ++it) { SurfacePointWithIndex* source = *it; double distance = point.distance(source); if(distance < best_total_distance) { best_interval = NULL; best_total_distance = distance; best_interval_position = 0.0; best_source_index = source->index(); } } } else if(point.type() == VERTEX) { vertex_pointer v = static_cast(point.base_element()); for(unsigned i=0; iadjacent_edges().size(); ++i) { edge_pointer e = v->adjacent_edges()[i]; list_pointer list = interval_list(e); double position = e->v0()->id() == v->id() ? 0.0 : e->length(); interval_pointer interval = list->covering_interval(position); if(interval) { double distance = interval->signal(position); if(distance < best_total_distance) { best_interval = interval; best_total_distance = distance; best_interval_position = position; best_source_index = interval->source_index(); } } } } if(best_total_distance > m_propagation_distance_stopped) //result is unreliable { best_total_distance = GEODESIC_INF; return NULL; } else { return best_interval; } } inline void GeodesicAlgorithmExact::trace_back(SurfacePoint& destination, //trace back piecewise-linear path std::vector& path) { path.clear(); double best_total_distance; double best_interval_position; unsigned source_index = std::numeric_limits::max(); interval_pointer best_interval = best_first_interval(destination, best_total_distance, best_interval_position, source_index); if(best_total_distance >= GEODESIC_INF/2.0) //unable to find the right path { return; } path.push_back(destination); if(best_interval) //if we did not hit the face source immediately { std::vector possible_edges; possible_edges.reserve(10); while(visible_from_source(path.back()) < 0) //while this point is not in the direct visibility of some source (if we are inside the FACE, we obviously hit the source) { SurfacePoint& q = path.back(); possible_traceback_edges(q, possible_edges); interval_pointer interval; double total_distance; double position; best_point_on_the_edge_set(q, possible_edges, interval, total_distance, position); //std::cout << total_distance + length(path) << std::endl; assert(total_distancesource_index(); edge_pointer e = interval->edge(); double local_epsilon = SMALLEST_INTERVAL_RATIO*e->length(); if(position < local_epsilon) { path.push_back(SurfacePoint(e->v0())); } else if(position > e->length()-local_epsilon) { path.push_back(SurfacePoint(e->v1())); } else { double normalized_position = position/e->length(); path.push_back(SurfacePoint(e, normalized_position)); } } } SurfacePoint& source = static_cast(m_sources[source_index]); if(path.back().distance(&source) > 0) { path.push_back(source); } } inline void GeodesicAlgorithmExact::print_statistics() { GeodesicAlgorithmBase::print_statistics(); unsigned interval_counter = 0; for(unsigned i=0; i IGL_INLINE void igl::exact_geodesic( const Eigen::MatrixBase &V, const Eigen::MatrixBase &F, const Eigen::MatrixBase &VS, const Eigen::MatrixBase &FS, const Eigen::MatrixBase &VT, const Eigen::MatrixBase &FT, Eigen::PlainObjectBase &D) { assert(V.cols() == 3 && F.cols() == 3 && "Only support 3D triangle mesh"); assert(VS.cols() <=1 && FS.cols() <= 1 && VT.cols() <= 1 && FT.cols() <=1 && "Only support one dimensional inputs"); std::vector points(V.rows() * V.cols()); std::vector faces(F.rows() * F.cols()); for (int i = 0; i < points.size(); i++) { points[i] = V(i / 3, i % 3); } for (int i = 0; i < faces.size(); i++) { faces[i] = F(i / 3, i % 3); } igl::geodesic::Mesh mesh; mesh.initialize_mesh_data(points, faces); igl::geodesic::GeodesicAlgorithmExact exact_algorithm(&mesh); std::vector source(VS.rows() + FS.rows()); std::vector target(VT.rows() + FT.rows()); for (int i = 0; i < VS.rows(); i++) { source[i] = (igl::geodesic::SurfacePoint(&mesh.vertices()[VS(i)])); } for (int i = 0; i < FS.rows(); i++) { source[i] = (igl::geodesic::SurfacePoint(&mesh.faces()[FS(i)])); } for (int i = 0; i < VT.rows(); i++) { target[i] = (igl::geodesic::SurfacePoint(&mesh.vertices()[VT(i)])); } for (int i = 0; i < FT.rows(); i++) { target[i] = (igl::geodesic::SurfacePoint(&mesh.faces()[FT(i)])); } exact_algorithm.propagate(source); std::vector path; D.resize(target.size(), 1); for (int i = 0; i < target.size(); i++) { exact_algorithm.trace_back(target[i], path); D(i) = igl::geodesic::length(path); } } #ifdef IGL_STATIC_LIBRARY template void igl::exact_geodesic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix>(Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::MatrixBase> const &, Eigen::PlainObjectBase> &); template void igl::exact_geodesic, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); #endif