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
16 changes: 16 additions & 0 deletions include/common/graph_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace graph_layout {
constexpr std::string_view ATTR_KEY_HEAD_LABEL = "headlabel";
constexpr std::string_view ATTR_KEY_LABEL_DISTANCE = "labeldistance";
constexpr std::string_view ATTR_KEY_SPLINES = "splines";
constexpr std::string_view ATTR_KEY_ARROW_HEAD = "arrowhead";
constexpr std::string_view ATTR_KEY_ARROW_TAIL = "arrowtail";

class Attribute {
public:
Expand Down Expand Up @@ -69,6 +71,15 @@ namespace graph_layout {
static const std::string SPLINE;
};

class AttributeArrowShape : public Attribute {
public:
using Attribute::Attribute;

static const std::string NONE;
static const std::string NORMAL;
static const std::string EMPTY;
};

class Attributes {
public:
Attributes() = default;
Expand Down Expand Up @@ -103,6 +114,11 @@ namespace graph_layout {
void setEdgeHeadLabel(int edgeId, const std::string& label);
void setEdgeLabelDistance(int edgeId, double scale);

void setEdgeDefaultArrowHead(const std::string& value);
void setEdgeDefaultArrowTail(const std::string& value);
void setEdgeArrowHead(int edgeId, const std::string& value);
void setEdgeArrowTail(int edgeId, const std::string& value);

private:
static const std::string MONOSPACE_FONT_FAMILY;

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"

[project]
name = "sp-graph-layout"
version = "1.2.0"
version = "1.3.0"
description = "A library for visualizing directed graphs."
requires-python = ">=3.8"
readme = "python/README.md"
Expand Down
10 changes: 10 additions & 0 deletions python/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
.def_property_readonly_static("RECT", [](py::object) { return AttributeShape::RECT; })
.def_property_readonly_static("RECORD", [](py::object) { return AttributeShape::RECORD; })
;
py::class_<AttributeArrowShape, Attribute>(m, "AttributeArrowShape")
.def(py::init<>())
.def_property_readonly_static("NONE", [](py::object) { return AttributeArrowShape::NONE; })
.def_property_readonly_static("NORMAL", [](py::object) { return AttributeArrowShape::NORMAL; })
.def_property_readonly_static("EMPTY", [](py::object) { return AttributeArrowShape::EMPTY; })
;
py::class_<Attributes>(m, "Attributes")
.def(py::init<>())
.def("set_rank_dir", &Attributes::setRankDir)
Expand All @@ -69,6 +75,10 @@ PYBIND11_MODULE(_core, m, py::mod_gil_not_used()) {
.def("set_vertex_default_shape", &Attributes::setVertexDefaultShape)
.def("set_vertex_default_monospace", &Attributes::setVertexDefaultMonospace)
.def("set_edge_default_monospace", &Attributes::setEdgeDefaultMonospace)
.def("set_edge_default_arrow_head", &Attributes::setEdgeDefaultArrowHead, py::arg("value"))
.def("set_edge_default_arrow_tail", &Attributes::setEdgeDefaultArrowTail, py::arg("value"))
.def("set_edge_arrow_head", &Attributes::setEdgeArrowHead, py::arg("edge_id"), py::arg("value"))
.def("set_edge_arrow_tail", &Attributes::setEdgeArrowTail, py::arg("edge_id"), py::arg("value"))
;
py::class_<DirectedGraphHierarchicalLayout>(m, "DirectedGraphHierarchicalLayout")
.def(py::init<>())
Expand Down
2 changes: 2 additions & 0 deletions python/wrapper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ._core import (
Attribute,
AttributeArrowShape,
AttributeRankDir,
Attributes,
AttributeShape,
Expand All @@ -21,6 +22,7 @@
"VertexPositioningMethod",
"SPDirectedGraph",
"Attribute",
"AttributeArrowShape",
"AttributeRankDir",
"AttributeShape",
"Attributes",
Expand Down
22 changes: 22 additions & 0 deletions src/common/graph_attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const string AttributeShape::RECORD = "record";
const string AttributeSplines::LINE = "line";
const string AttributeSplines::SPLINE = "spline";

const string AttributeArrowShape::NONE = "none";
const string AttributeArrowShape::NORMAL = "normal";
const string AttributeArrowShape::EMPTY = "empty";

const string Attributes::MONOSPACE_FONT_FAMILY = R"(ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace)";

unordered_map<string_view, string> Attributes::DEFAULT_GRAPH_ATTRIBUTE_VALUES = {
Expand All @@ -42,6 +46,8 @@ unordered_map<string_view, string> Attributes::DEFAULT_EDGE_ATTRIBUTE_VALUES = {
{ATTR_KEY_HEAD_LABEL, ""},
{ATTR_KEY_LABEL_DISTANCE, "2.0"},
{ATTR_KEY_SPLINES, "line"},
{ATTR_KEY_ARROW_HEAD, AttributeArrowShape::NORMAL},
{ATTR_KEY_ARROW_TAIL, AttributeArrowShape::NONE},
};

Attribute::Attribute() = default;
Expand Down Expand Up @@ -171,3 +177,19 @@ void Attributes::setEdgeHeadLabel(const int edgeId, const string& label) {
void Attributes::setEdgeLabelDistance(const int edgeId, const double scale) {
setEdgeAttributes(edgeId, ATTR_KEY_LABEL_DISTANCE, scale);
}

void Attributes::setEdgeDefaultArrowHead(const string& value) {
_edgeDefaultAttributes[ATTR_KEY_ARROW_HEAD] = value;
}

void Attributes::setEdgeDefaultArrowTail(const string& value) {
_edgeDefaultAttributes[ATTR_KEY_ARROW_TAIL] = value;
}

void Attributes::setEdgeArrowHead(const int edgeId, const string& value) {
setEdgeAttributes(edgeId, ATTR_KEY_ARROW_HEAD, value);
}

void Attributes::setEdgeArrowTail(const int edgeId, const string& value) {
setEdgeAttributes(edgeId, ATTR_KEY_ARROW_TAIL, value);
}
9 changes: 6 additions & 3 deletions src/directed/cross_minimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,16 @@ void CrossMinimization::reduceNumCrossWithBaryCenterHeuristic(SPDirectedGraph& g
for (const auto& edge : _graph.getInEdges(u)) {
weight += positions.find(edge.u)->second;
}
weight /= _graph.getInDegrees()[u];
if (const auto degree = _graph.getInDegrees()[u]; degree > 0) {
weight /= degree;
}
} else {
for (const auto& edge : _graph.getOutEdges(u)) {
weight += positions.find(edge.v)->second;
}
weight /= _graph.getOutDegrees()[u];
if (const auto degree = _graph.getOutDegrees()[u]; degree > 0) {
weight /= degree;
}
}
return weight;
};
Expand All @@ -299,7 +303,6 @@ void CrossMinimization::reduceNumCrossWithMedianHeuristic(SPDirectedGraph& graph
for (const auto& edge : _graph.getInEdges(u)) {
adjPositions.emplace_back(positions.find(edge.u)->second);
}
ranges::sort(adjPositions);
} else {
for (const auto& edge : _graph.getOutEdges(u)) {
adjPositions.emplace_back(positions.find(edge.v)->second);
Expand Down
16 changes: 10 additions & 6 deletions src/directed/vertex_positioning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ void VertexPositioning::sortIncidentEdges(SPDirectedGraph& graph, SPLayering& la
auto& outEdgeIds = graph.getOutEdgeIdsRef();
for (int i = 0; i < numLayers; i++) {
for (const auto u : layering.orders[i]) {
ranges::sort(inEdgeIds[u], [&](const int a, const int b) {
return layering.positions[i - 1][graph.getEdge(a).u] < layering.positions[i - 1][graph.getEdge(b).u];
});
ranges::sort(outEdgeIds[u], [&](const int a, const int b) {
return layering.positions[i + 1][graph.getEdge(a).v] < layering.positions[i + 1][graph.getEdge(b).v];
});
if (i > 0) {
ranges::sort(inEdgeIds[u], [&](const int a, const int b) {
return layering.positions[i - 1][graph.getEdge(a).u] < layering.positions[i - 1][graph.getEdge(b).u];
});
}
if (i + 1 < numLayers) {
ranges::sort(outEdgeIds[u], [&](const int a, const int b) {
return layering.positions[i + 1][graph.getEdge(a).v] < layering.positions[i + 1][graph.getEdge(b).v];
});
}
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/graph_layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ string DirectedGraphHierarchicalLayout::render() const {
e->setFont(_attributes.edgeAttributes(edge.id, ATTR_KEY_FONT_NAME), stod(_attributes.edgeAttributes(edge.id, ATTR_KEY_FONT_SIZE)));
e->setLabelDistance(stod(_attributes.edgeAttributes(edge.id, ATTR_KEY_LABEL_DISTANCE)));
e->setMargin(2);
e->setArrowHead();
e->setArrowHead(_attributes.edgeAttributes(edge.id, ATTR_KEY_ARROW_HEAD));
e->setArrowTail(_attributes.edgeAttributes(edge.id, ATTR_KEY_ARROW_TAIL));
if (edge.u != edge.v) {
if (outEdges[edge.v].contains(edge.u)) {
// There is a reverse edge
Expand Down Expand Up @@ -240,7 +241,8 @@ string DirectedGraphHierarchicalLayout::render() const {
e->setFont(_attributes.edgeAttributes(edgeId, ATTR_KEY_FONT_NAME), stod(_attributes.edgeAttributes(edgeId, ATTR_KEY_FONT_SIZE)));
e->setLabelDistance(stod(_attributes.edgeAttributes(edgeId, ATTR_KEY_LABEL_DISTANCE)));
e->setSplines(_attributes.edgeAttributes(edgeId, ATTR_KEY_SPLINES));
e->setArrowHead();
e->setArrowHead(_attributes.edgeAttributes(edgeId, ATTR_KEY_ARROW_HEAD));
e->setArrowTail(_attributes.edgeAttributes(edgeId, ATTR_KEY_ARROW_TAIL));
e->setMargin(2);
for (int i = 0; i + 1 < static_cast<int>(edgeIds.size()); ++i) {
const auto& edge = _graph->getEdge(edgeIds[i]);
Expand Down
10 changes: 10 additions & 0 deletions wasm/graph_layout_wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ EMSCRIPTEN_BINDINGS(GraphLayoutWASM) {
.class_property("RECT", &AttributeShape::RECT)
.class_property("RECORD", &AttributeShape::RECORD)
;
class_<AttributeArrowShape, base<Attribute>>("AttributeArrowShape")
.constructor<>()
.class_property("NONE", &AttributeArrowShape::NONE)
.class_property("NORMAL", &AttributeArrowShape::NORMAL)
.class_property("EMPTY", &AttributeArrowShape::EMPTY)
;
class_<Attributes>("Attributes")
.constructor<>()
.function("setRankDir", &Attributes::setRankDir)
Expand All @@ -63,6 +69,10 @@ EMSCRIPTEN_BINDINGS(GraphLayoutWASM) {
.function("setVertexDefaultShape", &Attributes::setVertexDefaultShape)
.function("setVertexDefaultMonospace", &Attributes::setVertexDefaultMonospace)
.function("setEdgeDefaultMonospace", &Attributes::setEdgeDefaultMonospace)
.function("setEdgeDefaultArrowHead", &Attributes::setEdgeDefaultArrowHead)
.function("setEdgeDefaultArrowTail", &Attributes::setEdgeDefaultArrowTail)
.function("setEdgeArrowHead", &Attributes::setEdgeArrowHead)
.function("setEdgeArrowTail", &Attributes::setEdgeArrowTail)
;
class_<DirectedGraphHierarchicalLayout>("DirectedGraphHierarchicalLayout")
.constructor<>()
Expand Down
14 changes: 14 additions & 0 deletions wasm/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,27 @@ declare module 'sp-graph-layout' {
static readonly RECORD: string
}

export class AttributeArrowShape extends Attribute {
constructor()
static readonly NONE: string
static readonly NORMAL: string
static readonly EMPTY: string
}

export class Attributes {
constructor()
setRankDir(rankDir: string): void
setVertexShape(u: number, shape: string): void
setEdgeTailLabel(edgeId: number, label: string): void
setEdgeHeadLabel(edgeId: number, label: string): void
setEdgeLabelDistance(edgeId: number, scale: number): void
setVertexDefaultShape(shape: string): void
setVertexDefaultMonospace(): void
setEdgeDefaultMonospace(): void
setEdgeDefaultArrowHead(value: string): void
setEdgeDefaultArrowTail(value: string): void
setEdgeArrowHead(edgeId: number, value: string): void
setEdgeArrowTail(edgeId: number, value: string): void
}

export class DirectedGraphHierarchicalLayout {
Expand Down
2 changes: 2 additions & 0 deletions wasm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const SPDirectedGraph = GraphLayoutWASM.SPDirectedGraph;
const Attribute = GraphLayoutWASM.Attribute;
const AttributeRankDir = GraphLayoutWASM.AttributeRankDir;
const AttributeShape = GraphLayoutWASM.AttributeShape;
const AttributeArrowShape = GraphLayoutWASM.AttributeArrowShape;
const Attributes = GraphLayoutWASM.Attributes;
const DirectedGraphHierarchicalLayout = GraphLayoutWASM.DirectedGraphHierarchicalLayout;

Expand All @@ -41,6 +42,7 @@ export {
Attribute,
AttributeRankDir,
AttributeShape,
AttributeArrowShape,
Attributes,
DirectedGraphHierarchicalLayout,
};
2 changes: 1 addition & 1 deletion wasm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sp-graph-layout",
"version": "1.2.0",
"version": "1.3.0",
"type": "module",
"main": "index.js",
"types": "index.d.ts",
Expand Down