I want to create a set (mathematically speaking, not std::set) of unique elements in C++. My elements are std::pair<int, int> and they represent an edge. Because those edges are not directed, I don't want to have duplicates like (3,4) and (4,3). How can I achieve this in C++ ?
Something along these lines, perhaps:
using Edge = std::pair<int, int>;
struct CompareEdges {
bool operator()(const Edge& a, const Edge& b) const {
return Normalize(a) < Normalize(b);
}
private:
Edge Normalize(const Edge& e) {
if (e.first <= e.second) return e;
return {e.second, e.first};
}
};
std::set<Edge, CompareEdges> SetOfEdges;
This is another solution, with the compare function as lambda expression.
using Edge = pair<int, int>;
std::set<Edge, std::function<bool(const Edge &, const Edge &)>> edges(
[](const Edge &a, const Edge &b)
{
const int x = min(a.first, a.second);
const int y = min(b.first, b.second);
if (x < y)
return true;
else if (y > x)
return false;
else
return max(a.first, a.second) < max(b.first, b.second);
}
);
Related
this is the continuation of another question I asked regarding boost graphs. (GraphML in Boost).
After successfully reading the graph, I am trying to apply boost A* on some random start and goal nodes but its throwing segmentation fault.
Following are the details of my graph.
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties>;
struct VertexProperties {
std::vector<double> joint_angles;
VertexProperties() : joint_angles(3){}
};
struct EdgeProperties {
double weight;
};
I used A* cities file from Boost as reference (A* Cities) to code my distance heuristic and astar_goal_visitor.
struct found_goal {}; // exception for termination
// visitor that terminates when we find the goal
template <typename Vertex>
class astar_goal_visitor : public boost::default_astar_visitor
{
public:
astar_goal_visitor(Vertex goal) : m_goal(goal) {}
template <class Graph>
void examine_vertex(Vertex u, Graph& g) {
if(u == m_goal)
throw found_goal();
}
private:
Vertex m_goal;
};
// euclidean distance heuristic
template <class Graph>
class distance_heuristic : public boost::astar_heuristic<typename Graph::Graph, double>
{
public:
typedef typename boost::graph_traits<typename Graph::Graph>::vertex_descriptor Vertex;
distance_heuristic(Vertex goal, Graph &graph)
: m_goal(goal), m_graph(graph) {}
double operator()(Vertex u)
{
double dx = m_graph.getGraph()[m_goal].joint_angles[0] - m_graph.getGraph()[u].joint_angles[0];
double dy = m_graph.getGraph()[m_goal].joint_angles[1] - m_graph.getGraph()[u].joint_angles[1];
double dz = m_graph.getGraph()[m_goal].joint_angles[2] - m_graph.getGraph()[u].joint_angles[2];
return ::sqrt(dx * dx + dy * dy + dz * dz);
}
private:
Graph m_graph;
Vertex m_goal;
};
As for astar_search parameters, the predecessor map is defined as below.
typedef boost::property_map < Graph, boost::vertex_index_t >::type IndexMap;
typedef boost::iterator_property_map < Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
BoostGraph::PredecessorMap BoostGraph::getPredecessorMap(){
IndexMap indexMap = boost::get(boost::vertex_index, graph);
std::vector<Vertex> p(boost::num_vertices(graph));
PredecessorMap predecessorMap(&p[0], indexMap);
return predecessorMap;
}
The final code for search is
std::vector<double> d(boost::num_vertices(graph.getGraph()));
std::mt19937 gen(time(0));
BoostGraph::Vertex start = boost::random_vertex(graph.getGraph(), gen);
BoostGraph::Vertex goal = boost::random_vertex(graph.getGraph(), gen);
auto weightmap = boost::get(&EdgeProperties::weight, graph.getGraph());
try {
// call astar named parameter interface
boost::astar_search
(graph.getGraph(), start,
distance_heuristic<BoostGraph>
(goal, graph),
boost::predecessor_map(graph.getPredecessorMap()).distance_map(boost::make_iterator_property_map(d.begin(), boost::get(boost::vertex_index, graph.getGraph()))).
weight_map(weightmap).
visitor(astar_goal_visitor<BoostGraph::Vertex>(goal)));
} catch(found_goal fg) { // found a path to the goal
std::list<BoostGraph::Vertex> shortest_path;
for(BoostGraph::Vertex v = goal;; v = p[v]) {
shortest_path.push_front(v);
if(p[v] == v)
break;
}
}
The getGraph function of Class BoostGraph is defined below where graph is the protected member of class BoostGraph.
protected:
Graph graph;
const BoostGraph::Graph& BoostGraph::getGraph() const{
return graph;
}
The segmentation fault is coming in stl_tree.h and I have no idea what has gone wrong. Any help would be appreciated. Thanks
Your heuristic holds a copy of the graph. You're indexing using vertex descriptors belonging to different graphs.
Your predecessor map is a local variable (the vector), and the map is a dangling reference to it after getPredecessorMap returns. Just make the vector a member, and then getPredecessorMap can be eliminated, because it doesn't really add much.
Also, you're indexing into joint_angles without bounds checking. Prefer .at(n) over [n] if you want to be safer. In fact, consider using std::array<double, 3> instead of std::vector<double>.
All in all I get the impression that you've been trying to hide complexity in a class and member functions, however it leads to the code becoming fragmented and inviting lots of unnecessary lifetime issues.
There are also parts of the code you do not show. They likely hold more problems (e.g. getGraph() is crucial).
Here's my simplified take:
Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/random.hpp>
using JointAngles = std::vector<double>;
struct VertexProperties {
JointAngles joint_angles{0, 0, 0};
};
struct EdgeProperties {
double weight = 0;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
VertexProperties, EdgeProperties>;
using Vertex = Graph::vertex_descriptor;
// visitor that terminates when we find the goal
struct goal_visitor : boost::default_astar_visitor {
struct found {}; // exception for termination
Vertex m_goal;
goal_visitor(Vertex g) : m_goal(g) {}
template <class Graph> void examine_vertex(Vertex u, Graph&) {
if (u == m_goal)
throw found{};
}
};
// euclidean distance heuristic
struct distance_heuristic : boost::astar_heuristic<Graph, double> {
distance_heuristic(Vertex goal, Graph& graph)
: m_graph(graph)
, m_goal(graph[goal].joint_angles) {}
double operator()(Vertex u) const {
auto& c = m_graph[u].joint_angles;
auto //
dx = m_goal.at(0) - c.at(0), //
dy = m_goal.at(1) - c.at(1), //
dz = m_goal.at(2) - c.at(2);
using std::sqrt; // allow ADL, good practice
return sqrt(dx * dx + dy * dy + dz * dz);
}
private:
Graph& m_graph; // reference!
JointAngles m_goal;
};
#include <random>
#include <fmt/ranges.h>
int main() {
Graph graph(4);
graph[0] = VertexProperties{{0, 0, 0}};
graph[1] = VertexProperties{{1, 1, 1}};
graph[2] = VertexProperties{{2, 2, 2}};
add_edge(0, 1, graph);
add_edge(1, 2, graph);
std::vector<Vertex> predecessors(num_vertices(graph));
std::vector<double> distances(num_vertices(graph));
auto index = get(boost::vertex_index, graph);
auto pmap = make_safe_iterator_property_map(predecessors.begin(), predecessors.size(), index);
auto dmap = make_safe_iterator_property_map(distances.begin(), distances.size(), index);
auto weightmap = get(&EdgeProperties::weight, graph);
std::mt19937 gen(std::random_device{}());
Vertex start = random_vertex(graph, gen);
Vertex goal = random_vertex(graph, gen);
try {
// call astar named parameter interface
astar_search( //
graph, start, distance_heuristic{goal, graph},
boost::predecessor_map(pmap) //
.distance_map(dmap)
.weight_map(weightmap)
.visitor(goal_visitor{goal}));
fmt::print("{} -> {}: No path\n", start, goal);
} catch (goal_visitor::found) {
std::list<Vertex> path;
for (auto cursor = goal;;) {
path.push_front(cursor);
auto previous = std::exchange(cursor, predecessors.at(cursor));
if (cursor == previous)
break;
}
fmt::print("{} -> {}: {}\n", start, goal, path);
}
}
Which prints e.g.
2 -> 1: [2, 1]
Encapsulation?
If you want to encapsulate, do it along the functional boundaries, instead of artificial boundaries that gave you the lifetime headaches you didn't need. If performance is no concern, you can reduce code with a facility like vector_property_map. For example:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/astar_search.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/random.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/property_map/vector_property_map.hpp>
#include <fmt/ranges.h>
#include <random>
class BoostGraph {
private:
using JointAngles = std::vector<double>;
struct VertexProperties {
JointAngles joint_angles{0, 0, 0};
};
struct EdgeProperties {
double weight = 0;
};
using Graph = boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
VertexProperties, EdgeProperties>;
using Vertex = Graph::vertex_descriptor;
public:
BoostGraph() : m_graph(4) {
// TODO read graph
m_graph[0] = VertexProperties{{0, 0, 0}};
m_graph[1] = VertexProperties{{1, 1, 1}};
m_graph[2] = VertexProperties{{2, 2, 2}};
add_edge(0, 1, m_graph);
add_edge(1, 2, m_graph);
}
friend std::ostream& operator<<(std::ostream& os, BoostGraph const& bg) {
auto name = boost::make_transform_value_property_map(
[ja = get(&VertexProperties::joint_angles, bg.m_graph)](Vertex v) {
return fmt::format("Vertex #{} ({})", v, ja[v]);
},
get(boost::vertex_index, bg.m_graph));
print_graph(bg.m_graph, name, os);
return os;
}
Vertex random_vertex() { return boost::random_vertex(m_graph, m_prng); }
std::list<Vertex> find_path(Vertex start, Vertex goal) const {
std::list<Vertex> path;
auto pmap = make_vector_property_map<Vertex>(get(boost::vertex_index, m_graph));
try {
astar_search( //
m_graph, start, distance_heuristic{goal, m_graph},
boost::predecessor_map(pmap) //
.weight_map(get(&EdgeProperties::weight, m_graph))
.visitor(finder{goal}));
} catch (finder::found) {
for (auto cursor = goal;;) {
path.push_front(cursor);
auto previous = std::exchange(cursor, pmap[cursor]);
if (cursor == previous)
break;
}
}
return path;
}
private:
// visitor that terminates when we find the goal
struct finder : boost::default_astar_visitor {
struct found {}; // exception for termination
Vertex m_goal;
finder(Vertex g) : m_goal(g) {}
void examine_vertex(Vertex u, Graph const&) const {
if (u == m_goal)
throw found{};
}
};
// euclidean distance heuristic
struct distance_heuristic : boost::astar_heuristic<Graph, double> {
distance_heuristic(Vertex goal, Graph const& graph)
: m_graph(graph)
, m_goal(graph[goal].joint_angles) {}
double operator()(Vertex u) const {
auto& c = m_graph[u].joint_angles;
auto //
dx = m_goal.at(0) - c.at(0), //
dy = m_goal.at(1) - c.at(1), //
dz = m_goal.at(2) - c.at(2);
using std::sqrt; // allow ADL, good practice
return sqrt(dx * dx + dy * dy + dz * dz);
}
private:
Graph const& m_graph; // reference!
JointAngles m_goal;
};
Graph m_graph;
std::mt19937 m_prng{std::random_device{}()};
};
int main() {
BoostGraph bg;
std::cout << "Graph: " << bg << "\n";
for (auto i = 0; i < 10; ++i) {
auto start = bg.random_vertex(), goal = bg.random_vertex();
auto path = bg.find_path(start, goal);
fmt::print("{} -> {}: {}\n", start, goal, path);
}
}
Printing e.g.
Graph: Vertex #0 ([0, 0, 0]) <--> Vertex #1 ([1, 1, 1])
Vertex #1 ([1, 1, 1]) <--> Vertex #0 ([0, 0, 0]) Vertex #2 ([2, 2, 2])
Vertex #2 ([2, 2, 2]) <--> Vertex #1 ([1, 1, 1])
Vertex #3 ([0, 0, 0]) <-->
2 -> 2: [2]
1 -> 0: [1, 0]
0 -> 1: [0, 1]
2 -> 0: [2, 1, 0]
3 -> 0: []
0 -> 3: []
0 -> 3: []
1 -> 0: [1, 0]
1 -> 0: [1, 0]
3 -> 1: []
I have a customized array implementation for a geometry (vertex) . Each element of the array is represented by the vertex which has a Point . Now I want to check the distance between each point for the vertex in the array . So essentially for every vertex in the array of size n I will loop till n and calculate the distance of vertex point with all n vertex points in the array . So a pseudo code will look like this
func MyFunc( Array iVrtxList , vrtx inpVertex )
{
point refPt = inpVertex->getPoint();
for ( i=0 ; i < iVrtxList.size(); i++)
{
if( distanceBetween(iVertexList(i).point ,rePt ) == 0 )
return
}
iVrtxList.add(inpVertex);
}
}
So I want to avoid N X N looping . I thought of sorting the container and then check only the subsequent element for the distance . However I seem to miss some elements
I kind of achieved our objective to skip the duplicate vertices without using a N X N approach . I used a multimap to keep track of the vertices . The key was hashed with the x,y,z values which we adjusted to precision to make it work with the dataset . However the hash calculation is vulnerable as any mapping causing collision will defeat the purpose . Following are the definitions
class PointCoords
{
public:
PointCoords(double ix, double iy, double iz) : _x(ix), _y(iy), _z(iz) { };
double _x;
double _y;
double _z;
};
class PointCoords_hash
{
public:
size_t operator()(const PointCoords& v) const
{
auto f1 = std::hash<double>{}(round(v._x * 10) / 10);
auto f2 = std::hash<double>{}(round(v._y * 10) / 10);
auto f3 = std::hash<double>{}(round(v._z * 10) / 10);
size_t hCode = (f1 ^ f2 ^ f3) << 1;
return ((f1 ^ f2 ^ f3) << 1);
};
};
class PointCoords_equal
{
public:
bool operator()(const PointCoords& u, const PointCoords& v) const
{
return (equal(u._x, v._x, 1e-6) &&
equal(u._y, v._y, 1e-6) &&
equal(u._z, v._z, 1e-6));
};
};
bool :equal( double d1, double d2, double err)
{
return ( fabs( d1 -d2) <= err);
}
I am interested in finding sets of vertices that are not ordered in a directed acyclic graph (in the sense of a topological order).
That is, for example: two vertices in non-connected subgraphs, or the pairs (B,C), (B,D) in cases such as :
The naive possibility I thought of was to enumerate all the topological sorts (in this case [ A, B, C, D ] and [ A, C, D, B ] & find all pairs whose order ends up being different in at least two sorts, but this would be pretty expensive computationally.
Are there other, faster possibilities for what I want to achieve ? I am using boost.graph.
Basically what you want is the pair of nodes (u,v) such that there is no path from u to v, and no path from v to u. You can find for each node, all nodes that are reachable from that node using DFS. Total Complexity O(n(n+m)).
Now all you have to do is for each pair check if neither of the 2 nodes are reachable by the other.
You can start with a simple topological sort. Boost's implementation conveniently returns a reverse ordered list of vertices.
You can iterate that list, marking each initial leaf node with a new branch id until a shared node is encountered.
Demo Time
Let's start with the simplests of graph models:
#include <boost/graph/adjacency_list.hpp>
using Graph = boost::adjacency_list<>;
We wish to map branches:
using BranchID = int;
using BranchMap = std::vector<BranchID>; // maps vertex id -> branch id
We want to build, map and visualize the mappings:
Graph build();
BranchMap map_branches(Graph const&);
void visualize(Graph const&, BranchMap const& branch_map);
int main() {
// sample data
Graph g = build();
// do the topo sort and distinguish branches
BranchMap mappings = map_branches(g);
// output
visualize(g, mappings);
}
Building Graph
Just the sample data from the question:
Graph build() {
Graph g(4);
enum {A,B,C,D};
add_edge(A, B, g);
add_edge(A, C, g);
add_edge(C, D, g);
return g;
}
Mapping The Branches
As described in the introduction:
#include <boost/graph/topological_sort.hpp>
std::vector<BranchID> map_branches(Graph const& g) {
std::vector<Vertex> reverse_topo;
boost::topological_sort(g, back_inserter(reverse_topo));
// traverse the output to map to unique branch ids
std::vector<BranchID> branch_map(num_vertices(g));
BranchID branch_id = 0;
for (auto v : reverse_topo) {
auto degree = out_degree(v, g);
if (0 == degree) // is leaf?
++branch_id;
if (degree < 2) // "unique" path
branch_map[v] = branch_id;
}
return branch_map;
}
Visualizing
Let's write a graph-viz representation with each branch colored:
#include <boost/graph/graphviz.hpp>
#include <iostream>
void visualize(Graph const& g, BranchMap const& branch_map) {
// display helpers
std::vector<std::string> const colors { "gray", "red", "green", "blue" };
auto name = [](Vertex v) -> char { return 'A'+v; };
auto color = [&](Vertex v) -> std::string { return colors[branch_map.at(v) % colors.size()]; };
// write graphviz:
boost::dynamic_properties dp;
dp.property("node_id", transform(name));
dp.property("color", transform(color));
write_graphviz_dp(std::cout, g, dp);
}
This uses a tiny shorthand helper to create the transforming property maps:
// convenience short-hand to write transformed property maps
template <typename F>
static auto transform(F f) { return boost::make_transform_value_property_map(f, boost::identity_property_map{}); };
To compile this on a non-c++14 compiler you can replace the call to transform with the expanded body
Full Listing
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
using Graph = boost::adjacency_list<>;
using BranchID = int;
using BranchMap = std::vector<BranchID>; // maps vertex id -> branch id
Graph build();
BranchMap map_branches(Graph const&);
void visualize(Graph const&, BranchMap const& branch_map);
int main() {
// sample data
Graph g = build();
// do the topo sort and distinguish branches
BranchMap mappings = map_branches(g);
// output
visualize(g, mappings);
}
using Vertex = Graph::vertex_descriptor;
Graph build() {
Graph g(4);
enum {A,B,C,D};
add_edge(A, B, g);
add_edge(A, C, g);
add_edge(C, D, g);
return g;
}
#include <boost/graph/topological_sort.hpp>
std::vector<BranchID> map_branches(Graph const& g) {
std::vector<Vertex> reverse_topo;
boost::topological_sort(g, back_inserter(reverse_topo));
// traverse the output to map to unique branch ids
std::vector<BranchID> branch_map(num_vertices(g));
BranchID branch_id = 0;
for (auto v : reverse_topo) {
auto degree = out_degree(v, g);
if (0 == degree) // is leaf?
++branch_id;
if (degree < 2) // "unique" path
branch_map[v] = branch_id;
}
return branch_map;
}
#include <boost/property_map/transform_value_property_map.hpp>
// convenience short-hand to write transformed property maps
template <typename F>
static auto transform(F f) { return boost::make_transform_value_property_map(f, boost::identity_property_map{}); };
#include <boost/graph/graphviz.hpp>
#include <iostream>
void visualize(Graph const& g, BranchMap const& branch_map) {
// display helpers
std::vector<std::string> const colors { "gray", "red", "green", "blue" };
auto name = [](Vertex v) -> char { return 'A'+v; };
auto color = [&](Vertex v) -> std::string { return colors[branch_map.at(v) % colors.size()]; };
// write graphviz:
boost::dynamic_properties dp;
dp.property("node_id", transform(name));
dp.property("color", transform(color));
write_graphviz_dp(std::cout, g, dp);
}
Printing
digraph G {
A [color=gray];
B [color=red];
C [color=green];
D [color=green];
A->B ;
A->C ;
C->D ;
}
And the rendered graph:
Summary
Nodes in branches with different colors cannot be compared.
For Learning Purposes:
I am creating a small numerical methods library and I am trying to implement the gradient currently I have done 2D gradient and 3D gradient . But I want to generalize this to higher dimensions.
Currently I have :
matrix<double> analysis::gradient_2D(std::function<double(double, double)> fn, double x, double y)
{
matrix<double> R(2, 1);
std::function<double(double)> fnX = [fn, y](double xVar){ return fn(xVar, y); };
std::function<double(double)> fnY = [fn, x](double yVar){ return fn(x, yVar); };
R(1, 1) = differentiateBest(fnX, x);
R(1, 2) = differentiateBest(fnY, y);
return R;
}
matrix<double> analysis::gradient_3D(std::function<double(double, double, double)> fn, double x, double y, double z)
{
matrix<double> R(3, 1);
std::function<double(double)> fnX = [fn, y, z](double xVar){ return fn(xVar, y,z); };
std::function<double(double)> fnY = [fn, x, z](double yVar){ return fn(x ,yVar, z); };
std::function<double(double)> fnZ = [fn, x, y](double zVar){ return fn(x, y, zVar); };
R(1, 1) = differentiateBest(fnX, x);
R(1, 2) = differentiateBest(fnY, y);
R(1, 3) = differentiateBest(fnZ, z);
return R;
}
// Where
double analysis::differentiateBest(std::function<double(double)> fn, double x)
{
return derivative_1_Richardson_6O(fn, x);
}
// For brevity , derivative_1_Richardson_6O also has the same input as differentiateBest
I know it is verbose , but I like it
Question
What I would like to do is to create a
// What do I do at the ... ?
matrix<double> analysis::gradient_ND(std::function<double(...)> fn, matrix<double>)
So that I can pass a std::function with arbitrary input say N and I will pass
a vector which has N values.
How will I go about doing this ? If the answer is too long , links will be appreciated too .
Thank you.
PS: I saw a method using Templates , but if I use templates in the implementation , the I will have to change the .cpp file to something else right ? I would like to avoid. If using templates is the only way , then I will have to compromise. Thanks.
template<class T>
struct array_view {
T* b = nullptr; T* e = nullptr;
T* begin() const { return b; }
T* end() const { return e; }
size_t size() const { return end()-begin(); }
bool empty() const { return begin()==end(); }
T& front()const{return *begin(); }
T& back()const{return *std::prev(end()); }
T& operator[](size_t i)const{return begin()[i]; }
array_view( T* s, T* f ):b(s),e(f) {};
array_view() = default;
array_view( T* s, size_t l ):array_view(s, s+l) {}
using non_const_T = std::remove_const_t<T>;
array_view( std::initializer_list<non_const_T> il ):
array_view(il.begin(), il.end()) {}
template<size_t N>
array_view( T(&arr)[N] ):array_view(arr, N){}
template<size_t N>
array_view( std::array<T,N>&arr ):array_view(arr.data(), N){}
template<size_t N>
array_view( std::array<non_const_T,N> const&arr ):
array_view(arr.data(), N){}
template<class A>
array_view( std::vector<T,A>& v):
array_view(v.data(), v.size()){}
template<class A>
array_view( std::vector<non_const_T,A> const& v):
array_view(v.data(), v.size()){}
};
an array_view is a non-owning view into a contiguous array of T. It has converting constructors from a myriad of contiguous containers (if matrix is contiguous, a converter to array_view should be written).
Then:
matrix<double> analysis::gradient_ND(std::function<double(array_view<const double>)>, array_view<const double> pt)
is reasonable.
Using array_view causes no problem with .h and .cpp code splitting. The only templates involved are either fixed (the array_view itself), or are resolved when constructing the array_view.
matrix<double> R(pt.size(), 1);
auto make_tmp = [pt]{
std::vector<double> tmp;
tmp.reserve(pt.size());
for (double x:pt)
tmp.push_back(x);
return tmp;
};
std::vector<std::function<double(double)>> partials;
partials.reserve(pt.size());
for (size_t i = 0; i < pt.size(); ++i) {
partials.push_back(
[&,i](double x){ auto tmp=make_tmp(); tmp[i]=x; return fn(tmp); };
);
}
for (size_t i = 0; i < pt.size(); ++i) {
R(1, i) = differentiateBest(partials[i], pt[i]);
}
return R;
note that creating array of partials is not actually needed. You could just directly differentiateBest.
There is an inefficiency where partials reallocate each call. If you are ok with making reentrancy not work (which will often be ok), creating a tmp and capturing it by-value, and modifying and restoring it after each call to fn, would boost performance.
[&,i,tmp=make_tmp()](double x){ std::swap(tmp[i],x); double r=fn(tmp); std::swap(tmp[i],x); return r; };
is C++14 version. C++11 version would create a tmp variable and capture it by-value.
I cannot find out an efficient way to generate the opposite edges of a given edge. My idea is just to do the iterates:
//construct the opposite half edges
for(int j=0;j<edge_num;j++)
for(int m=0;m<edge_num;m++)
if(edge[j].vert_end->v_index==edge[m].vert_start->v_index &&
edge[j].vert_start->v_index==edge[m].vert_end->v_index )
{
edge[j].pair = &edge[m];
edge[m].pair = &edge[j];
}
Other information about an half edge is generated from the procedure of loading .M file.
My structure is:
class HE_vert{
public:
GLfloat x, y, z;
int v_index;
HE_edge *edge;
};
class HE_face{
public:
int v1, v2, v3;
int f_index;
HE_edge* edge;
};
class HE_edge{
public:
HE_edge(){ pair = NULL; }
public:
HE_vert* vert_start; // vertex at the start of the half-edge
HE_vert* vert_end; // vertex at the end of the half-edge
HE_edge* pair; // oppositely oriented adjacent half-edge
HE_face* face; // face the half-edge borders
HE_edge* next; // next half-edge around the face
int e_index;
};
I checked all the output information and it’s correct, but it took a long computational time, especially when loading bunny.M. How can I do this in an more efficient way? Could you give me some hints?
// grid[i + vert_num*j] = edge from i to j
int grid[vert_num*vert_num]; // malloc()?
// memset()?
for (int i = vert_num*vert_num - 1; i >= 0; i--)
{
grid[i] = -1;
}
for (int i = 0; i < edge_num; i++)
{
int i_from = edge[i]->vert_start->v_index;
int i_to = edge[i]->vert_end->v_index;
int pair_index = grid[i_to + vert_num*i_from];
if (pair_index >= 0)
{
edge[i]->pair = edge[pair_index];
edge[pair_index]->pair = edge[i];
grid[i_to + vert_num*i_from] = -1;
}
else
{
grid[i_from + vert_num*i_to] = i;
}
}
Possible optimization: Use a linked list instead of a huge array. There will only be about 1-4 entries for each row/column.