Add Planar graph [WIP]

This commit is contained in:
Arthur Sonzogni
2018-03-18 15:51:42 +01:00
parent a17708df9a
commit c02f3dd80b
8 changed files with 474 additions and 65 deletions
+2 -2
View File
@@ -34,13 +34,13 @@ if (Web)
# Emscripten options
string(APPEND CMAKE_CXX_FLAGS " -Os")
string(APPEND CMAKE_CXX_FLAGS " -Os")
string(APPEND CMAKE_CXX_FLAGS " -s WASM=1")
string(APPEND CMAKE_CXX_FLAGS " -s DEMANGLE_SUPPORT=1")
string(APPEND CMAKE_CXX_FLAGS " --js-opts 3")
string(APPEND CMAKE_CXX_FLAGS " --llvm-lto 3")
string(APPEND CMAKE_CXX_FLAGS " --llvm-opts 3")
string(APPEND CMAKE_CXX_FLAGS " -s ASSERTIONS=0")
string(APPEND CMAKE_CXX_FLAGS " -s ASSERTIONS=2")
string(APPEND CMAKE_CXX_FLAGS " --closure 1")
string(APPEND CMAKE_CXX_FLAGS " --no-heap-copy")
string(APPEND CMAKE_CXX_FLAGS " -s EXPORTED_FUNCTIONS='[\"_translate\"]'")
Symlink
+1
View File
@@ -0,0 +1 @@
/usr/include/boost
+2
View File
@@ -0,0 +1,2 @@
const char* test_directory = "/home/sonzogna/programmation/real/Diagon/src/test";
const char* git_version = "21";
+36 -32
View File
@@ -60,7 +60,7 @@
<div class="gutter"></div>
<!--3 Errors-->
<textarea id="errors" placeholder='errors' readonly rows=4 ></textarea>
<textarea id="errors" placeholder='errors' readonly rows=8 ></textarea>
</div>
</div>
@@ -77,23 +77,6 @@ let errors = document.getElementById('errors');
let tools_data = [];
errors.value = '';
function print(text) {
if (arguments.length > 1)
text = Array.prototype.slice.call(arguments).join(' ');
errors.value += text + '\n';
};
window.Module = {
preRun: [],
postRun: [],
print: print,
printErr: print,
canvas: undefined,
};
</script>
<script src="main.js"></script>
<script>
function ClearSelectBox(select_box) {
while(select_box.options.length > 0)
@@ -132,27 +115,48 @@ function UpdateInput() {
}
UpdateOutput();
}
let translate = Module.cwrap('translate', 'void', ['string','string']);
let translate = function(){};
function UpdateOutput() {
errors.value = '';
console.log("tool", tools_data[tools.value].tool, input.value);
console.log(translate);
translate(tools_data[tools.value].tool, input.value);
}
tools.addEventListener("input", UpdateExamples);
examples.addEventListener("input", UpdateInput);
tools.addEventListener("input", UpdateOutput);
input.addEventListener('input', UpdateOutput);
function OnRuntimeInitialized() {
tools.addEventListener("input", UpdateExamples);
examples.addEventListener("input", UpdateInput);
tools.addEventListener("input", UpdateOutput);
input.addEventListener('input', UpdateOutput);
fetch("tools.json")
.then(response => response.json())
.then(data => {
tools_data = data;
UpdateTools();
UpdateExamples()
UpdateInput();
UpdateOutput();
})
fetch("tools.json")
.then(response => response.json())
.then(data => {
tools_data = data;
UpdateTools();
UpdateExamples()
UpdateInput();
UpdateOutput();
translate = Module.cwrap('translate', 'void', ['string','string']);
})
}
function print(text) {
if (arguments.length > 1)
text = Array.prototype.slice.call(arguments).join(' ');
errors.value += text + '\n';
};
window.Module = {
preRun: [],
postRun: [],
print: print,
printErr: print,
canvas: undefined,
onRuntimeInitialized: OnRuntimeInitialized,
};
</script>
<script src="main.js"></script>
</html>
+66
View File
@@ -58,6 +58,7 @@
}
]
},
{
"tool": "PlanarGraph",
"description": "Planar graph",
@@ -65,6 +66,71 @@
{
"title": "------------------------",
"empty": true
},
{
"title":"if then else loop",
"content": [
"if -> \"then A\" -> end",
"if -> \"then B\" -> end",
"end -> loop -> if"
]
},
{
"title":"test",
"content": [
"A -- B",
"A -- C",
"A -- D -- G",
"B -- Z",
"C -- Z"
]
},
{
"title": "Big graph",
"content": [
"a -- b",
"a -- c",
"a -- d",
"b -- c",
"b -- e",
"c -- e",
"c -- f",
"d -- f",
"d -- g",
"e -- h",
"f -- h",
"f -- i",
"f -- j",
"f -- g",
"g -- k",
"h -- o",
"h -- l",
"i -- l",
"i -- m",
"i -- j",
"j -- m",
"j -- n",
"j -- k",
"k -- n",
"k -- r",
"l -- o",
"l -- m",
"m -- o",
"m -- p",
"m -- n",
"n -- q",
"n -- r",
"o -- s",
"o -- p",
"p -- s",
"p -- t",
"p -- q",
"q -- t",
"q -- r",
"r -- t",
"s -- z",
"t -- z"
]
}
]
},
@@ -1,5 +1,19 @@
ANTLR(PlanarGraph.g4)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED OFF)
set(Boost_USE_STATIC_RUNTIME ON)
#find_package(Boost)
#include_directories(${Boost_INCLUDE_DIRS}/boost/graph)
#include_directories(${Boost_INCLUDE_DIRS})
#message(STATUS ${Boost_INCLUDE_DIRS})
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs})
message(STATUS "dir='${dir}'")
endforeach()
add_library(translator_planar_graph STATIC
PlanarGraphLexer.cpp
PlanarGraphParser.cpp
+321 -24
View File
@@ -1,4 +1,5 @@
#include <iostream>
#include <queue>
#include <sstream>
#include "screen/Screen.h"
@@ -6,10 +7,79 @@
#include "translator/planar_graph/PlanarGraphLexer.h"
#include "translator/planar_graph/PlanarGraphParser.h"
// With emscripten, you may need to execute in the src directory.
// # ln -s /usr/include/boost/ boost
#include <boost/graph/make_biconnected_planar.hpp>
#include <boost/graph/make_connected.hpp>
#include <boost/graph/make_maximal_planar.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/boyer_myrvold_planar_test.hpp>
#include <boost/graph/chrobak_payne_drawing.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/planar_canonical_ordering.hpp>
#include <boost/graph/properties.hpp>
#include <boost/property_map/property_map.hpp>
using Graph = boost::adjacency_list<boost::vecS,
boost::vecS,
boost::undirectedS,
boost::property<boost::vertex_index_t, int>,
boost::property<boost::edge_index_t, int>>;
using Embedding =
std::vector<std::vector<boost::graph_traits<Graph>::edge_descriptor>>;
using Coordinates = struct {
size_t x;
size_t y;
};
using StraightLineDrawing = std::vector<Coordinates>;
std::unique_ptr<Translator> PlanarGraphTranslator() {
return std::make_unique<PlanarGraph>();
}
struct PlanarGraph::DrawnEdge {
int x;
int vertex_up;
int vertex_down;
int y_up;
int y_down;
void Draw(Screen& screen, PlanarGraph& graph) {
if (graph.arrow_style[vertex_up][vertex_down] == ArrowStyle::NONE)
return;
int top = 3 * y_up - 1;
int bottom = 3 * y_down + 3;
screen.DrawVerticalLine(top + 1, bottom - 1, x);
if (graph.arrow_style[vertex_down][vertex_up] == ArrowStyle::LINE)
screen.DrawPixel(x, top, U'');
else
screen.DrawPixel(x, top, U'');
if (graph.arrow_style[vertex_up][vertex_down] == ArrowStyle::LINE)
screen.DrawPixel(x, bottom, U'');
else
screen.DrawPixel(x, bottom, U'');
}
};
struct PlanarGraph::DrawnVertex {
int left;
int right;
int y;
std::wstring text;
void Draw(Screen& screen) {
screen.DrawBox(left, 3 * y, right - left + 1, 3);
int text_position = left + 1 + (right - left - 1 - text.size()) / 2;
screen.DrawText(text_position, 3 * y + 1, text);
}
std::vector<DrawnEdge> edges;
};
void PlanarGraph::Process(const std::string& input) {
Read(input);
Write();
@@ -23,13 +93,6 @@ void PlanarGraph::Read(const std::string& input) {
antlr4::CommonTokenStream tokens(&lexer);
tokens.fill();
// for (auto token : tokens.getTokens()) {
// std::cout << token->toString() << std::endl;
//}
// Parser.
//output_ = parser.graph()->toStringTree(&parser);
PlanarGraphParser parser(&tokens);
ReadGraph(parser.graph());
}
@@ -49,8 +112,8 @@ void PlanarGraph::ReadEdges(PlanarGraphParser::EdgesContext* edges) {
for (PlanarGraphParser::ArrowContext* arrow : edges->arrow()) {
arrows.push_back(ReadArrow(arrow));
}
for(int i = 0; i<arrows.size(); ++i) {
vertex.push_back(Vertex{nodes[i], nodes[i+1], arrows[i]});
for (int i = 0; i < arrows.size(); ++i) {
vertex.push_back(Edge{nodes[i], nodes[i + 1], arrows[i]});
}
}
@@ -76,8 +139,8 @@ int PlanarGraph::ReadNode(PlanarGraphParser::NodeContext* node) {
}
}
PlanarGraph::Arrow PlanarGraph::ReadArrow(PlanarGraphParser::ArrowContext *
arrow) {
PlanarGraph::Arrow PlanarGraph::ReadArrow(
PlanarGraphParser::ArrowContext* arrow) {
if (arrow->RIGHT_ARROW())
return Arrow::RIGHT;
if (arrow->NONE_ARROW())
@@ -91,23 +154,257 @@ PlanarGraph::Arrow PlanarGraph::ReadArrow(PlanarGraphParser::ArrowContext *
}
std::wstring PlanarGraph::ArrowToString(PlanarGraph::Arrow arrow) {
switch(arrow) {
case Arrow::RIGHT: return L"->";
case Arrow::NONE: return L"--";
case Arrow::LEFT_RIGHT: return L"<->";
case Arrow::LEFT: return L"<-";
switch (arrow) {
case Arrow::RIGHT:
return L"->";
case Arrow::NONE:
return L"--";
case Arrow::LEFT_RIGHT:
return L"<->";
case Arrow::LEFT:
return L"<-";
}
}
void InitializeEdgeIndex(Graph& graph) {
boost::property_map<Graph, boost::edge_index_t>::type e_index =
boost::get(boost::edge_index, graph);
boost::graph_traits<Graph>::edges_size_type edge_count = 0;
boost::graph_traits<Graph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = boost::edges(graph); ei != ei_end; ++ei)
boost::put(e_index, *ei, edge_count++);
}
void Print(Graph& graph) {
std::cout << "-----" << std::endl;
auto edges = boost::edges(graph);
for (auto edge = edges.first; edge != edges.second; ++edge) {
std::cout << boost::source(*edge, graph) << "->" << boost::target(*edge, graph) << std::endl;
}
std::cout << "-----" << std::endl;
}
bool ComputePlanarEmbedding(const Graph& graph, Embedding& embedding) {
embedding = Embedding(boost::num_vertices(graph));
return boost::boyer_myrvold_planarity_test(
boost::boyer_myrvold_params::graph = graph,
boost::boyer_myrvold_params::embedding = embedding.data());
}
void PlanarGraph::Write() {
int width = 30;
int height = 30;
Screen screen(width, height);
int y = 0;
for(int i = 0; i<id_to_name.size(); ++i) {
screen.DrawBoxedText(0,y,id_to_name[i]);
y+=4;
if (id_to_name.size() <= 2)
return;
int num_vertices = id_to_name.size();
// Compute ArrowStyle
for(auto& v : vertex) {
switch(v.arrow) {
case Arrow::RIGHT:
arrow_style[v.from][v.to] = ArrowStyle::ARROW;
arrow_style[v.to][v.from] = ArrowStyle::LINE;
break;
case Arrow::LEFT_RIGHT:
arrow_style[v.from][v.to] = ArrowStyle::ARROW;
arrow_style[v.to][v.from] = ArrowStyle::ARROW;
break;
case Arrow::LEFT:
arrow_style[v.from][v.to] = ArrowStyle::LINE;
arrow_style[v.to][v.from] = ArrowStyle::ARROW;
break;
case Arrow::NONE:
arrow_style[v.from][v.to] = ArrowStyle::LINE;
arrow_style[v.to][v.from] = ArrowStyle::LINE;
break;
}
}
output_ = screen.ToString();
// Create a graph.
std::cout << id_to_name.size() << " nodes and " << num_vertices<< " vertex" << std::endl;
Graph graph(id_to_name.size());
for (auto& it : vertex)
add_edge(it.from, it.to, graph);
InitializeEdgeIndex(graph);
Graph initial_graph(graph);
Print(graph);
// Make it connected.
boost::make_connected(graph);
InitializeEdgeIndex(graph);
// Make it biconnected.
Embedding embedding;
bool is_planar = ComputePlanarEmbedding(graph, embedding);
std::cout << "is_planar = " << is_planar << std::endl;
if (!is_planar)
return;
boost::make_biconnected_planar(graph, embedding.data());
InitializeEdgeIndex(graph);
Graph biconnected_graph(graph);
ComputePlanarEmbedding(graph, embedding);
boost::make_maximal_planar(graph, embedding.data());
InitializeEdgeIndex(graph);
// Find a canonical ordering.
ComputePlanarEmbedding(graph, embedding);
std::vector<boost::graph_traits<Graph>::vertex_descriptor> ordering;
boost::planar_canonical_ordering(graph, embedding.data(),
std::back_inserter(ordering));
std::cout << "Num vertices = " << num_vertices << std::endl;
std::vector<size_t> inverse_ordering(num_vertices);
for(int i = 0; i<inverse_ordering.size(); ++i) {
inverse_ordering[ordering[i]] = i;
}
for(auto i : ordering) {
std::cout << "ordering = " << i << std::endl;
}
StraightLineDrawing straight_line_drawing(num_vertices);
// Compute the straight line drawing
boost::chrobak_payne_straight_line_drawing(graph, embedding,
ordering.begin(), ordering.end(),
straight_line_drawing.data());
Print(graph);
graph = initial_graph;
// Compute {down,up}_children
std::vector<std::vector<int>> down_children(num_vertices);
std::vector<std::vector<int>> up_children(num_vertices);
auto compare_with = [&](int i) {
return [&, i](int a, int b) {
int a_dx = straight_line_drawing[a].x - straight_line_drawing[i].x;
int a_dy = straight_line_drawing[a].y - straight_line_drawing[i].y;
int b_dx = straight_line_drawing[b].x - straight_line_drawing[i].x;
int b_dy = straight_line_drawing[b].y - straight_line_drawing[i].y;
return a_dx * b_dy - b_dx * a_dy < 0;
};
};
for (int i = 0; i < num_vertices; ++i) {
auto& down = down_children[i];
auto& up = up_children[i];
auto adjacent = boost::adjacent_vertices(i, graph);
for (auto j = adjacent.first; j != adjacent.second; ++j) {
if (inverse_ordering[i] < inverse_ordering[*j]) {
down.push_back(*j);
} else {
up.push_back(*j);
}
}
std::sort(down.begin(), down.end(), compare_with(i));
std::sort(up.begin(), up.end(), compare_with(i));
}
// Compute Y.
std::vector<size_t> y(num_vertices, 0);
for (size_t i : ordering) {
auto adjacent = boost::adjacent_vertices(i, graph);
for (auto j = adjacent.first; j != adjacent.second; ++j) {
if (inverse_ordering[i] < inverse_ordering[*j]) {
y[*j] = std::max(y[*j], y[i] + 1);
}
}
}
std::vector<DrawnVertex> drawn_vertices(boost::num_vertices(graph));
std::vector<bool> is_drawn(boost::num_vertices(graph), false);
std::vector<int> x(y.size(),-1);
std::function<void(int)> refresh_x = [&](int y) {
int i = y + 1;
while (x[i] < x[y] && i < x.size()) {
x[i] = x[y];
++i;
};
};
auto Draw = [&]() {
// Determine the screen dimension
int width = 0;
int height = 0;
for (int i = 0; i < num_vertices; ++i) {
if (!is_drawn[i])
continue;
width = std::max(width, drawn_vertices[i].right + 1);
height = std::max(height, 3 * drawn_vertices[i].y + 3);
}
Screen screen(width, height);
for (int i = 0; i < num_vertices; ++i) {
if (!is_drawn[i])
continue;
drawn_vertices[i].Draw(screen);
}
for (int i = 0; i < num_vertices; ++i) {
if (!is_drawn[i])
continue;
for (auto& edge : drawn_vertices[i].edges) {
edge.Draw(screen, *this);
}
}
output_ += screen.ToString();
output_ += "------\n";
};
std::function<void(int)> DrawNode = [&](int i) -> void {
if (is_drawn[i])
return;
DrawnVertex& vertex = drawn_vertices[i];
int child_left = -1;
int child_right = -1;
// Draw our childrens.
for (auto j : down_children[i]) {
DrawNode(j);
auto& child = drawn_vertices[j];
DrawnEdge edge;
edge.vertex_up = i;
edge.vertex_down = j;
edge.y_up = y[i] + 1;
edge.y_down = y[j] - 1;
// Make the child bigger if we need to.
child.right = std::max(child.right, x[y[j]-1]+3);
x[y[j]] = child.right;
refresh_x(y[j]);
edge.x = child.left + 1;
edge.x = std::max(edge.x, x[y[i]] + 2);
if (y[j] != y[i] + 1) {
edge.x = std::max(edge.x, x[child.y - 1] + 1);
}
vertex.edges.push_back(edge);
x[y[i]+1] = std::max(x[y[i]+1], edge.x);
refresh_x(y[i]+1);
if (child_left == -1) {
child_left = edge.x - 1;
}
child_right = edge.x + 1;
}
// Draw ourself.
vertex.text = id_to_name[i];
vertex.y = y[i];
vertex.left = std::max(0, std::max(child_left, x[y[i]] + 1));
vertex.right =
std::max(child_right, (int)(vertex.left + vertex.text.size() + 1));
x[y[i]] = vertex.right;
refresh_x(y[i]);
// Mark ourself as drawn.
is_drawn[i] = true;
};
for (int i : ordering) {
if (y[i] == 0) {
DrawNode(i);
}
}
Draw();
}
+32 -7
View File
@@ -8,6 +8,15 @@
#include "translator/Translator.h"
#include "translator/planar_graph/PlanarGraphParser.h"
struct Box {
int left;
int right;
int top;
int bottom;
static Box Union(Box A, Box B);
static Box Translate(Box A, int x, int y);
};
class PlanarGraph : public Translator {
public:
virtual ~PlanarGraph() = default;
@@ -16,6 +25,12 @@ class PlanarGraph : public Translator {
//----------------------------------------------------------------------------
private:
std::string output_;
std::map<std::wstring, int> name_to_id;
std::vector<std::wstring> id_to_name;
int next_id = 0;
enum class Arrow {
RIGHT,
LEFT_RIGHT,
@@ -23,19 +38,27 @@ class PlanarGraph : public Translator {
LEFT,
};
struct Vertex {
struct Edge {
int from;
int to;
Arrow arrow;
};
std::vector<Edge> vertex;
std::vector<Vertex> vertex;
std::map<std::wstring, int> name_to_id;
std::vector<std::wstring> id_to_name;
int next_id = 0;
enum class ArrowStyle {
NONE,
LINE,
ARROW
};
std::map<int, std::map<int, ArrowStyle>> arrow_style;
std::string output_;
struct DrawnEdge;
struct DrawnVertex;
struct Node {
int id;
std::vector<Node> childs;
};
//----------------------------------------------------------------------------
void Read(const std::string& input);
@@ -45,6 +68,8 @@ class PlanarGraph : public Translator {
Arrow ReadArrow(PlanarGraphParser::ArrowContext* arrow);
void Write();
std::wstring ArrowToString(Arrow arrow);
std::vector<std::vector<Edge>> ConnectedComponent(
const std::vector<Edge>& vertex);
//----------------------------------------------------------------------------
};