import/export graph to plain text

master
dmatetelki 8 years ago
parent f4b80c3ec9
commit a50f8ba2df

@ -0,0 +1,59 @@
#include "graph.hpp"
#include <stdexcept>
#include <fstream>
// format: 1 line = 1 node
// first line followed by it's neighbours.
// separator is an empty new line
// std::string vertexSerializer(const V& v) converts V into std::string
// V vertexCreator(const std::string&s) converts std::string to V
template <typename V, typename F>
Graph<V> readGraphFromPlainText(const std::string& filename, F vertexCreator)
{
std::ifstream file(filename);
if (!file.good())
throw std::runtime_error("Failed to open " + filename + " to read.");
Graph<V> g;
std::string line;
while (std::getline(file, line))
if (!line.empty())
break;
bool new_entry = true;
V current_vertex;
while (std::getline(file, line)) {
if (line.empty()) {
new_entry = true;
} else {
if (new_entry) {
current_vertex = vertexCreator(line);
g.addVertex(current_vertex);
new_entry = false;
} else {
g.addEdge(current_vertex, vertexCreator(line));
}
}
}
return g;
}
template <typename V, typename F>
void writeGraphToPlainText(const Graph<V>& g, const std::string& filename, F vertexSerializer)
{
std::ofstream file;
file.open (filename);
if (!file.is_open())
throw std::runtime_error("Failed to open " + filename + " to write.");
for (const auto cit : g) {
file << vertexSerializer(cit) << std::endl;
for (const auto cit2 : g.neighboursOf(cit))
file << vertexSerializer(cit2) << std::endl;
file << std::endl;
}
file.close();
}

@ -8,6 +8,7 @@ graph/test_priority_queue.cpp
graph/test_quad_tree.cpp
graph/test_graph_algorithms.cpp
graph/test_marching_squares.cpp
graph/test_plaintext.cpp
test_main.cpp)

@ -4,6 +4,8 @@
#include <graph/graph.hpp>
#include <cmath>
#include <iomanip>
#include <sstream>
inline std::size_t hash_f(float f) { return std::hash<float>()(f); }
inline std::size_t hash_2f(float f1, float f2) { return hash_f(f1) ^ (hash_f(f2) << 1); }
@ -20,8 +22,17 @@ struct float2 {
inline bool operator ==(const float2& v1, const float2& v2) { return v1.x == v2.x && v1.y == v2.y; }
inline bool operator !=(const float2& v1, const float2& v2) { return !(v1 == v2); }
inline float pow2(float f) { return f*f; }
inline float dist(const float2& v1, const float2& v2) { return sqrt(pow2(v2.x - v1.x) + pow2(v2.y - v1.y)); }
inline float distance(const float2& v1, const float2& v2) { return sqrt(pow2(v2.x - v1.x) + pow2(v2.y - v1.y)); }
inline float lenght(const float2& v) { return sqrt(pow2(v.x) + pow2(v.y)); }
inline float angle(const float2& v) { return asin(v.y /lenght(v)); }
inline bool operator <(const float2& v1, const float2& v2)
{
const float l1 = lenght(v1);
const float l2 = lenght(v2);
if (l1 < l2) return true;
if (l1 > l2) return false;
return (angle(v1) < angle(v2));
}
namespace std {
template <>
@ -33,13 +44,34 @@ namespace std {
class distanceOf2float2s : public std::function<float(float2, float2)>
{
public:
float operator()(float2 a, float2 b) { return dist(a, b); }
float operator()(float2 a, float2 b) { return distance(a, b); }
};
class distanceOf2ints : public std::function<int(int, int)>
{
public:
float operator()(int a, int b) { return abs(a-b); }
};
template <typename T>
std::string to_string_with_precision(const T a_value, const int n = 6)
{
std::ostringstream out;
out << std::fixed << std::setprecision(n) << a_value;
return out.str();
}
}
inline float2 float2creator(const std::string& line)
{
std::stringstream ss(line);
float f1, f2;
ss >> f1 >> f2;
return float2(f1, f2);
}
inline std::string float2serializer(const float2& f2)
{
return std::to_string_with_precision(f2.x, 3) + " " + std::to_string_with_precision(f2.y, 3);
}
constexpr std::size_t numberOfEdges(std::size_t number_of_rows, std::size_t number_of_columns) {
@ -68,7 +100,7 @@ std::vector<typename Graph<V>::Edge>* createEdges(std::size_t number_of_rows, st
retval->reserve(number_of_edges);
// inside
for (std::size_t c = 1; c < number_of_columns-1; ++c)
for (std::size_t c = 1; c < number_of_columns-1; ++c) {
for (std::size_t r = 1; r < number_of_rows-1; ++r) {
retval->push_back(typename Graph<V>::Edge(float2(r, c), float2(r-1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, c), float2(r-1, c)));
@ -79,36 +111,38 @@ std::vector<typename Graph<V>::Edge>* createEdges(std::size_t number_of_rows, st
retval->push_back(typename Graph<V>::Edge(float2(r, c), float2(r+1, c)));
retval->push_back(typename Graph<V>::Edge(float2(r, c), float2(r+1, c+1)));
}
}
// top & bottom row
for (std::size_t r = 1; r < number_of_rows-1; ++r) {
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r-1, 0)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r+1, 0)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r-1, 1)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r, 1)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r+1, 1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r-1, number_of_columns-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r+1, number_of_columns-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r-1, number_of_columns-1-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r, number_of_columns-1-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r+1, number_of_columns-1-1)));
}
// top & bottom row
for (std::size_t r = 1; r < number_of_rows-1; ++r) {
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r-1, 0)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r+1, 0)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r-1, 1)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r, 1)));
retval->push_back(typename Graph<V>::Edge(float2(r, 0), float2(r+1, 1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r-1, number_of_columns-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r+1, number_of_columns-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r-1, number_of_columns-1-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r, number_of_columns-1-1)));
retval->push_back(typename Graph<V>::Edge(float2(r, number_of_columns-1), float2(r+1, number_of_columns-1-1)));
}
// left & right column
for (std::size_t c = 1; c < number_of_columns-1; ++c) {
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(0, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(0, c+1)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(1, c)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(1, c+1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1, c+1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1-1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1-1, c)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1-1, c+1)));
}
// left & right column
for (std::size_t c = 1; c < number_of_columns-1; ++c) {
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(0, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(0, c+1)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(1, c)));
retval->push_back(typename Graph<V>::Edge(float2(0, c), float2(1, c+1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1, c+1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1-1, c-1)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1-1, c)));
retval->push_back(typename Graph<V>::Edge(float2(number_of_rows-1, c), float2(number_of_rows-1-1, c+1)));
}
// corners
retval->push_back(typename Graph<V>::Edge(float2(0, 0), float2(0, 1)));

@ -0,0 +1,51 @@
#include <graph/graph_plaintext.hpp>
#include "../catch.hpp"
#include "fixture.hpp"
#include <cstdio> // remove file
inline int intCreator(const std::string& s) { return std::stoi(s); }
inline std::string intSerializer(int i) { return std::to_string(i); }
/// @note is there a smareter way to do this?
inline std::string s2s(const std::string& s) { return s; }
TEST_CASE( "Plain text import/export", "[IO]" ) {
SECTION("Invalid files") {
const std::string root_file("/root/graph_dump.txt");
const Graph<int> g1;
CHECK_THROWS ( writeGraphToPlainText(g1, root_file, intSerializer) );
CHECK_THROWS ( readGraphFromPlainText<int>(root_file, intCreator) );
}
SECTION("vertices are strings") {
const std::string fileName("/tmp/graph_dump.txt");
const std::vector<std::string> v{ "one", "two", "three", "... and four"};
Graph<std::string> g1(v);
g1.addEdge(v[0], v[1]);
g1.addEdge(v[0], v[2]);
g1.addEdge(v[2], v[3]);
writeGraphToPlainText(g1, fileName, s2s);
const Graph<std::string> g2 = readGraphFromPlainText<std::string>(fileName, s2s);
REQUIRE ( g1 == g2 );
remove(fileName.c_str());
}
SECTION("vertices are float coordinates") {
const std::string fileName("/tmp/graph_dump.txt");
const std::vector<typename Graph<float2>::Edge>* edges = createEdges<float2>(4, 4);
const Graph<float2> g1(*edges);
writeGraphToPlainText(g1, fileName, float2serializer);
const Graph<float2> g2 = readGraphFromPlainText<float2>(fileName, float2creator);
REQUIRE ( g1 == g2 );
remove(fileName.c_str());
delete edges;
}
}
Loading…
Cancel
Save