#include <catch2/catch.hpp>
#include <cmath>
#include <fmt/format.h>
+#include <unordered_map>
#include "svg_analyzer.h"
#include "test_edge_node_overlap_utilities.h"
// check maximum overlap of edge stem and arrowhead
if (check_options.check_max_edge_stem_arrow_overlap) {
const auto max_edge_stem_arrowhead_overlap =
- check_options.max_edge_stem_arrow_overlap;
+ check_options.max_edge_stem_arrow_overlap.at(
+ graph_options.primitive_arrowhead_shape);
DO_CHECK(edge_stem_arrowhead_overlap <=
max_edge_stem_arrowhead_overlap +
check_options.svg_rounding_error * 2);
// check maximum overlap of edge stem and arrowtail
if (check_options.check_max_edge_stem_arrow_overlap) {
const auto max_edge_stem_arrowtail_overlap =
- check_options.max_edge_stem_arrow_overlap;
+ check_options.max_edge_stem_arrow_overlap.at(
+ graph_options.primitive_arrowtail_shape);
DO_CHECK(edge_stem_arrowtail_overlap <=
max_edge_stem_arrowtail_overlap +
check_options.svg_rounding_error * 2);
static std::string generate_dot(const graph_options &graph_options) {
// use a semi-transparent color to easily see overlaps
const auto color = "\"#00000060\"";
- return fmt::format("digraph g1 {{"
- " graph [rankdir={}]"
- " node [penwidth={} shape={} color={} fontname=Courier]"
- " edge [penwidth={} color={} dir={}]"
- " a -> b"
- "}}",
- graph_options.rankdir, graph_options.node_penwidth,
- graph_options.node_shape, color,
- graph_options.edge_penwidth, color, graph_options.dir);
+ const auto arrowhead = fmt::format("{}{}", graph_options.arrowhead_modifier,
+ graph_options.primitive_arrowhead_shape);
+ const auto arrowtail = fmt::format("{}{}", graph_options.arrowtail_modifier,
+ graph_options.primitive_arrowtail_shape);
+ return fmt::format(
+ "digraph g1 {{"
+ " graph [rankdir={}]"
+ " node [penwidth={} shape={} color={} fontname=Courier]"
+ " edge [penwidth={} color={} dir={} arrowhead={} arrowtail={}]"
+ " a -> b"
+ "}}",
+ graph_options.rankdir, graph_options.node_penwidth,
+ graph_options.node_shape, color, graph_options.edge_penwidth, color,
+ graph_options.dir, arrowhead, arrowtail);
}
void test_edge_node_overlap(const graph_options &graph_options,
const double graphviz_max_svg_rounding_error =
std::pow(10, -graphviz_num_decimals_in_svg) / 2;
+ const std::unordered_map<std::string_view, double>
+ max_edge_stem_arrow_overlap = {
+ {"box",
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"crow",
+ // FIXME: adjust this when `crow` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"curve",
+ // FIXME: adjust this when `curve` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"diamond",
+ // FIXME: adjust this when `diamond` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"dot",
+ // FIXME: adjust this when `dot` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"icurve",
+ // FIXME: adjust this when `icurve` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"inv",
+ [&]() { // FIXME: calculate this accurately for normal/inv arrow
+ const auto tip_scale = 2 * 1.5135442928; // empirical value
+ return graph_options.edge_penwidth / 2 * tip_scale +
+ graphviz_bezier_clip_margin;
+ }()},
+ {"none",
+ // FIXME: adjust this when `none` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"normal",
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"tee",
+ // FIXME: adjust this when `tee` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ {"vee",
+ // FIXME: adjust this when `vee` is fixed for penwidth
+ graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin},
+ };
+
const auto extra_max_node_edge_overlap =
has_corner_in_rank_direction_unknown_during_layout(
graph_options.node_shape, graph_options.rankdir)
.max_node_edge_overlap =
graphviz_bezier_clip_margin + extra_max_node_edge_overlap,
.min_node_edge_overlap = 0,
- .max_edge_stem_arrow_overlap =
- graph_options.edge_penwidth / 2 + graphviz_bezier_clip_margin,
+ .max_edge_stem_arrow_overlap = max_edge_stem_arrow_overlap,
.min_edge_stem_arrow_overlap = 0,
.svg_rounding_error = graphviz_max_svg_rounding_error,
};
#include <string>
#include <string_view>
+#include <unordered_map>
#include "svg_analyzer.h"
double max_node_edge_overlap;
/// minimum required overlap between edge and node
double min_node_edge_overlap;
- /// maximum allowed overlap between edge stem and arrow
- double max_edge_stem_arrow_overlap;
+ /// map between primitive arrow shape and maximum allowed overlap between edge
+ /// stem and arrow
+ std::unordered_map<std::string_view, double> max_edge_stem_arrow_overlap;
/// minimum required overlap between edge stem and arrow
double min_edge_stem_arrow_overlap;
/// rounding error caused by limited precision in SVG attribute values
double node_penwidth = 1;
std::string_view dir = "forward";
double edge_penwidth = 1;
+ std::string_view primitive_arrowhead_shape = "normal";
+ std::string_view primitive_arrowtail_shape = "normal";
+ std::string_view arrowhead_modifier = "";
+ std::string_view arrowtail_modifier = "";
};
struct write_options {