Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src-cpp/graphviz/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ set_property(SOURCE main.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINA
# --- --- ---

set(SRCS
main.hpp
main.cpp
util.hpp
util.cpp
)

include_directories(
Expand Down
211 changes: 56 additions & 155 deletions src-cpp/graphviz/main.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
#include "main.hpp"

#include <gvc.h>
// #include <globals.h>
#include <gvplugin.h>
#include <graphviz_version.h>
#include <fstream>
// #include <cgraph++/AGraph.h>
// #include <gvc++/GVContext.h>
// #include <gvc++/GVLayout.h>
// #include <gvc++/GVRenderData.h>

#include <emscripten.h>

Expand Down Expand Up @@ -40,11 +34,11 @@ lt_symlist_t lt_preloaded_symbols[] = {
{"gvplugin_core_LTX_library", &gvplugin_core_LTX_library},
{0, 0}};

char lastErrorStr[1024];
StringBuffer lastErrorStr;

int vizErrorf(char *buf)
{
strncpy(lastErrorStr, buf, sizeof(lastErrorStr) - 1);
lastErrorStr = buf;
return 0;
}

Expand All @@ -67,6 +61,11 @@ Graphviz::Graphviz(int yInvert, int nop)
{
Y_invert = yInvert > 0 ? yInvert : origYInvert;
Nop = nop > 0 ? nop : origNop;

lastErrorStr = "";
agseterr(AGERR);
agseterrf(vizErrorf);
agreadline(1);
}

Graphviz::~Graphviz()
Expand All @@ -86,182 +85,84 @@ void Graphviz::createFile(const char *path, const char *data)
path, data);
}

const char *Graphviz::lastResult()
{
return m_result.c_str();
}

const char *Graphviz::layout(const char *src, const char *format, const char *engine)
{
lastErrorStr[0] = '\0';
m_result = "";

// const auto demand_loading = false;
// auto gvc = std::make_shared<GVC::GVContext>(lt_preloaded_symbols, demand_loading);
// auto g = std::make_shared<CGraph::AGraph>(dot);
// const auto layout = GVC::GVLayout(gvc, g, engine);
// const auto result = layout.render(format);
// m_result = result.string_view();

// return m_result.c_str();
layout_result = "";

GVC_t *gvc = gvContextPlugins(lt_preloaded_symbols, true);

agseterr(AGERR);
agseterrf(vizErrorf);

agreadline(1);

Agraph_t *graph;
char *data = NULL;
unsigned int length;
while ((graph = agmemread(src)))
Agraph_t *graph = agmemread(src);
if (graph)
{
if (data == NULL)
{
gvLayout(gvc, graph, engine);
gvRenderData(gvc, graph, format, &data, &length);
gvFreeLayout(gvc, graph);
}

char *data = NULL;
unsigned int length;

gvLayout(gvc, graph, engine);
gvRenderData(gvc, graph, format, &data, &length);
layout_result = data;
gvFreeRenderData(data);
gvFreeLayout(gvc, graph);
agclose(graph);

src = "";
}
m_result = data;
gvFreeRenderData(data);

gvFinalize(gvc);
gvFreeContext(gvc);

return m_result.c_str();
}

int myindegree(Agnode_t *n)
{
return agdegree(n->root, n, TRUE, FALSE);
return layout_result;
}

/* need outdegree without selfarcs */
int myoutdegree(Agnode_t *n)
bool Graphviz::acyclic(const char *src, bool doWrite, bool verbose)
{
Agedge_t *e;
int rv = 0;
acyclic_outFile = "";
acyclic_num_rev = 0;
bool retVal = false;

for (e = agfstout(n->root, n); e; e = agnxtout(n->root, e))
Agraph_t *graph = agmemread(src);
if (graph)
{
if (agtail(e) != aghead(e))
rv++;
TempFileBuffer outFile;
graphviz_acyclic_options_t opts = {outFile, doWrite, verbose};
retVal = graphviz_acyclic(graph, &opts, &acyclic_num_rev);
acyclic_outFile = outFile;
agclose(graph);
}
return rv;
}

bool isleaf(Agnode_t *n)
{
return myindegree(n) + myoutdegree(n) == 1;
return retVal;
}

bool ischainnode(Agnode_t *n)
void Graphviz::tred(const char *src, bool verbose, bool printRemovedEdges)
{
return myindegree(n) == 1 && myoutdegree(n) == 1;
}

void adjustlen(Agedge_t *e, Agsym_t *sym, int newlen)
{
char buf[12];
tred_out = "";
tred_err = "";

snprintf(buf, sizeof(buf), "%d", newlen);
agxset(e, sym, buf);
Agraph_t *graph = agmemread(src);
if (graph)
{
TempFileBuffer out;
TempFileBuffer err;
graphviz_tred_options_t opts = {verbose, printRemovedEdges, out, err};
graphviz_tred(graph, &opts);
tred_out = out;
tred_err = err;
agclose(graph);
}
}

Agsym_t *bindedgeattr(Agraph_t *g, const char *str)
const char *Graphviz::unflatten(const char *src, int maxMinlen, bool do_fans, int chainLimit)
{
return agattr(g, AGEDGE, const_cast<char *>(str), "");
}
unflatten_out = "";

const char *Graphviz::unflatten(const char *src, unsigned int MaxMinlen, bool Do_fans, unsigned int ChainLimit)
{
lastErrorStr[0] = '\0';
m_result = "";
Agraph_t *g = agmemread(src);
if (g)
Agraph_t *graph = agmemread(src);
if (graph)
{
graphviz_unflatten_options_t opts = {do_fans, maxMinlen, chainLimit};
graphviz_unflatten(graph, &opts);

Agnode_t *ChainNode;
unsigned int ChainSize = 0;

Agnode_t *n;
Agedge_t *e;
char *str;
Agsym_t *m_ix, *s_ix;
int cnt, d;

m_ix = bindedgeattr(g, "minlen");
s_ix = bindedgeattr(g, "style");

for (n = agfstnode(g); n; n = agnxtnode(g, n))
{
d = myindegree(n) + myoutdegree(n);
if (d == 0)
{
if (ChainLimit < 1)
continue;
if (ChainNode)
{
e = agedge(g, ChainNode, n, const_cast<char *>(""), TRUE);
agxset(e, s_ix, "invis");
ChainSize++;
if (ChainSize < ChainLimit)
ChainNode = n;
else
{
ChainNode = NULL;
ChainSize = 0;
}
}
else
ChainNode = n;
}
else if (d > 1)
{
if (MaxMinlen < 1)
continue;
cnt = 0;
for (e = agfstin(g, n); e; e = agnxtin(g, e))
{
if (isleaf(agtail(e)))
{
str = agxget(e, m_ix);
if (str[0] == 0)
{
adjustlen(e, m_ix, cnt % MaxMinlen + 1);
cnt++;
}
}
}

cnt = 0;
for (e = agfstout(g, n); e; e = agnxtout(g, e))
{
if (isleaf(e->node) || (Do_fans && ischainnode(e->node)))
{
str = agxget(e, m_ix);
if (str[0] == 0)
adjustlen(e, m_ix, cnt % MaxMinlen + 1);
cnt++;
}
}
}
}
FILE *fp = fopen("tmp.dot", "w");
agwrite(g, fp);
fclose(fp);
std::ifstream file("tmp.dot");
std::string graph_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
remove("tmp.dot");
m_result = graph_str;
TempFileBuffer tempFile;
agwrite(graph, tempFile);
unflatten_out = tempFile;
agclose(graph);
}
return m_result.c_str();
return unflatten_out;
}

// Include JS Glue ---
Expand Down
18 changes: 14 additions & 4 deletions src-cpp/graphviz/main.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#include <string>
#include "util.hpp"

class Graphviz
{
private:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think you could also remove this line.

std::string m_result;

public:
static const char *version();
static const char *lastError();
Expand All @@ -13,7 +12,18 @@ class Graphviz
~Graphviz();

void createFile(const char *path, const char *data);
const char *lastResult();

StringBuffer layout_result;
const char *layout(const char *dot, const char *format, const char *engine);
const char *unflatten(const char *dot, unsigned int MaxMinlen = 0, bool Do_fans = false, unsigned int ChainLimit = 0);

StringBuffer acyclic_outFile;
size_t acyclic_num_rev;
bool acyclic(const char *dot, bool doWrite = false, bool verbose = false);

StringBuffer tred_out;
StringBuffer tred_err;
void tred(const char *dot, bool verbose = false, bool printRemovedEdges = false);

StringBuffer unflatten_out;
const char *unflatten(const char *dot, int maxMinlen = 0, bool do_fans = false, int chainLimit = 0);
};
12 changes: 11 additions & 1 deletion src-cpp/graphviz/main.idl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ interface Graphviz
[Const] static DOMString version();
[Const] static DOMString lastError();
void createFile([Const] DOMString file, [Const] DOMString data);
[Const] DOMString lastResult();

[Const] attribute DOMString layout_result;
[Const] DOMString layout([Const] DOMString dot, [Const] DOMString format, [Const] DOMString engine);

[Const] attribute DOMString acyclic_outFile;
attribute long acyclic_num_rev;
boolean acyclic([Const] DOMString dot, boolean doWrite, boolean verbose);

[Const] attribute DOMString tred_out;
[Const] attribute DOMString tred_err;
void tred([Const] DOMString dot, boolean verbose, boolean printRemovedEdges);

[Const] DOMString unflatten([Const] DOMString dot, long MaxMinlen, boolean Do_fans, long ChainLimit);
};
55 changes: 55 additions & 0 deletions src-cpp/graphviz/util.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "util.hpp"

StringBuffer::operator std::string() const
{
return m_buffer;
}

StringBuffer::operator const char *() const
{
return m_buffer.c_str();
}

StringBuffer &StringBuffer::operator=(const std::string &str)
{
m_buffer = str;
return *this;
}

TempFileBuffer::TempFileBuffer()
{
if (std::tmpnam(tempFileName) == nullptr)
throw std::runtime_error("Failed to generate a unique temporary file name.");

filePointer = std::fopen(tempFileName, "w+");
if (filePointer == nullptr)
throw std::runtime_error("Failed to open temporary file for writing.");
}

TempFileBuffer::~TempFileBuffer()
{
if (filePointer != nullptr)
{
std::fclose(filePointer);
std::remove(tempFileName);
}
}

TempFileBuffer::operator FILE *() const
{
return filePointer;
}

TempFileBuffer::operator std::string() const
{
std::string content;
if (filePointer != nullptr)
{
std::rewind(filePointer);
char buffer[256];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe you want to use BUFSIZ for the size here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to remove the buff and loop altogether...

size_t bytesRead;
while ((bytesRead = std::fread(buffer, 1, sizeof(buffer), filePointer)) > 0)
content.append(buffer, bytesRead);
}
return content;
}
Loading