Commit 7c40bdcd authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

graph_draw(): Improve performance of X11 interactive drawing

parent fdc1c377
...@@ -95,7 +95,8 @@ enum edge_attr_t { ...@@ -95,7 +95,8 @@ enum edge_attr_t {
EDGE_FONT_SLANT, EDGE_FONT_SLANT,
EDGE_FONT_WEIGHT, EDGE_FONT_WEIGHT,
EDGE_FONT_SIZE, EDGE_FONT_SIZE,
EDGE_SLOPPY EDGE_SLOPPY,
EDGE_SEAMLESS
}; };
enum vertex_shape_t { enum vertex_shape_t {
...@@ -133,7 +134,7 @@ typedef std::tuple<double, double, double, double> color_t; ...@@ -133,7 +134,7 @@ typedef std::tuple<double, double, double, double> color_t;
typedef std::unordered_map<int, boost::any> attrs_t; typedef std::unordered_map<int, boost::any> attrs_t;
#endif #endif
typedef boost::mpl::map41< typedef boost::mpl::map42<
boost::mpl::pair<boost::mpl::int_<VERTEX_SHAPE>, vertex_shape_t>, boost::mpl::pair<boost::mpl::int_<VERTEX_SHAPE>, vertex_shape_t>,
boost::mpl::pair<boost::mpl::int_<VERTEX_COLOR>, color_t>, boost::mpl::pair<boost::mpl::int_<VERTEX_COLOR>, color_t>,
boost::mpl::pair<boost::mpl::int_<VERTEX_FILL_COLOR>, color_t>, boost::mpl::pair<boost::mpl::int_<VERTEX_FILL_COLOR>, color_t>,
...@@ -174,7 +175,8 @@ typedef boost::mpl::map41< ...@@ -174,7 +175,8 @@ typedef boost::mpl::map41<
boost::mpl::pair<boost::mpl::int_<EDGE_FONT_SLANT>, int32_t>, boost::mpl::pair<boost::mpl::int_<EDGE_FONT_SLANT>, int32_t>,
boost::mpl::pair<boost::mpl::int_<EDGE_FONT_WEIGHT>, int32_t>, boost::mpl::pair<boost::mpl::int_<EDGE_FONT_WEIGHT>, int32_t>,
boost::mpl::pair<boost::mpl::int_<EDGE_FONT_SIZE>, double>, boost::mpl::pair<boost::mpl::int_<EDGE_FONT_SIZE>, double>,
boost::mpl::pair<boost::mpl::int_<EDGE_SLOPPY>, uint8_t> > boost::mpl::pair<boost::mpl::int_<EDGE_SLOPPY>, uint8_t>,
boost::mpl::pair<boost::mpl::int_<EDGE_SEAMLESS>, uint8_t> >
attr_types; attr_types;
namespace std namespace std
...@@ -803,28 +805,35 @@ public: ...@@ -803,28 +805,35 @@ public:
cr.save(); cr.save();
cr.translate(_pos.first, _pos.second); cr.translate(_pos.first, _pos.second);
if (_attrs.template get<uint8_t>(VERTEX_HALO) && !outline) if (!outline && _attrs.template get<uint8_t>(VERTEX_HALO))
{ {
color_t c = _attrs.template get<color_t>(VERTEX_HALO_COLOR); color_t c = _attrs.template get<color_t>(VERTEX_HALO_COLOR);
double hs = _attrs.template get<double>(VERTEX_HALO_SIZE); double hs = _attrs.template get<double>(VERTEX_HALO_SIZE);
cr.set_source_rgba(get<0>(c), get<1>(c), get<2>(c), get<3>(c)); cr.set_source_rgba(get<0>(c), get<1>(c), get<2>(c), get<3>(c));
cr.save(); if (aspect != 1.)
cr.scale(aspect, 1.0); {
cr.save();
cr.scale(aspect, 1.0);
}
cr.arc(0, 0, size * hs / 2, 0, 2 * M_PI); cr.arc(0, 0, size * hs / 2, 0, 2 * M_PI);
cr.fill(); cr.fill();
cr.restore(); if (aspect != 1.)
cr.restore();
} }
boost::python::object osrc = _attrs.template get<boost::python::object>(VERTEX_SURFACE); boost::python::object osrc = _attrs.template get<boost::python::object>(VERTEX_SURFACE);
if (osrc == boost::python::object()) if (osrc == boost::python::object())
{ {
pw =_attrs.template get<double>(VERTEX_PENWIDTH); if (!outline)
pw = get_user_dist(cr, pw); {
cr.set_line_width(pw); pw =_attrs.template get<double>(VERTEX_PENWIDTH);
pw = get_user_dist(cr, pw);
cr.set_line_width(pw);
color = _attrs.template get<color_t>(VERTEX_COLOR); color = _attrs.template get<color_t>(VERTEX_COLOR);
cr.set_source_rgba(get<0>(color), get<1>(color), get<2>(color), cr.set_source_rgba(get<0>(color), get<1>(color), get<2>(color),
get<3>(color)); get<3>(color));
}
size_t nsides = 0; size_t nsides = 0;
vertex_shape_t shape = _attrs.template get<vertex_shape_t>(VERTEX_SHAPE); vertex_shape_t shape = _attrs.template get<vertex_shape_t>(VERTEX_SHAPE);
...@@ -832,20 +841,28 @@ public: ...@@ -832,20 +841,28 @@ public:
{ {
case SHAPE_CIRCLE: case SHAPE_CIRCLE:
case SHAPE_DOUBLE_CIRCLE: case SHAPE_DOUBLE_CIRCLE:
cr.save(); if (aspect != 1.)
cr.scale(aspect, 1.0); {
cr.save();
cr.scale(aspect, 1.0);
}
cr.arc(0, 0, size / 2., 0, 2 * M_PI); cr.arc(0, 0, size / 2., 0, 2 * M_PI);
cr.close_path(); cr.close_path();
cr.restore(); if (aspect != 1.)
cr.restore();
if (shape == SHAPE_DOUBLE_CIRCLE && !outline) if (shape == SHAPE_DOUBLE_CIRCLE && !outline)
{ {
cr.stroke(); cr.stroke();
cr.save(); if (aspect != 1.)
cr.scale(aspect, 1.0); {
cr.save();
cr.scale(aspect, 1.0);
}
cr.arc(0, 0, min(size / 2 - 2 * pw, cr.arc(0, 0, min(size / 2 - 2 * pw,
size * 0.8 / 2), size * 0.8 / 2),
0, 2 * M_PI); 0, 2 * M_PI);
cr.restore(); if (aspect != 1.)
cr.restore();
} }
break; break;
case SHAPE_PIE: case SHAPE_PIE:
...@@ -858,10 +875,8 @@ public: ...@@ -858,10 +875,8 @@ public:
} }
else else
{ {
cr.save();
cr.arc(0, 0, size / 2., 0, 2 * M_PI); cr.arc(0, 0, size / 2., 0, 2 * M_PI);
cr.close_path(); cr.close_path();
cr.restore();
} }
} }
break; break;
...@@ -880,18 +895,26 @@ public: ...@@ -880,18 +895,26 @@ public:
nsides = shape - SHAPE_TRIANGLE + 3; nsides = shape - SHAPE_TRIANGLE + 3;
if (nsides > 8) if (nsides > 8)
nsides -= 7; nsides -= 7;
cr.save(); if (aspect != 1.)
cr.scale(aspect, 1.0); {
cr.save();
cr.scale(aspect, 1.0);
}
draw_polygon(nsides, size / 2, cr); draw_polygon(nsides, size / 2, cr);
cr.restore(); if (aspect != 1.)
cr.restore();
if (shape >= SHAPE_DOUBLE_TRIANGLE && !outline) if (shape >= SHAPE_DOUBLE_TRIANGLE && !outline)
{ {
cr.stroke(); cr.stroke();
cr.save(); if (aspect != 1.)
cr.scale(aspect, 1.0); {
cr.save();
cr.scale(aspect, 1.0);
}
draw_polygon(nsides, min(size / 2 - 2 * pw, draw_polygon(nsides, min(size / 2 - 2 * pw,
size * 0.8 / 2), cr); size * 0.8 / 2), cr);
cr.restore(); if (aspect != 1.)
cr.restore();
} }
break; break;
default: default:
...@@ -936,69 +959,72 @@ public: ...@@ -936,69 +959,72 @@ public:
} }
} }
string text = _attrs.template get<string>(VERTEX_TEXT); if (!outline)
if (text != "" && !outline)
{ {
cr.save(); string text = _attrs.template get<string>(VERTEX_TEXT);
if (text != "")
{
cr.save();
double text_pos = 0; double text_pos = 0;
double text_rotation = 0; double text_rotation = 0;
vector<double> text_offset; vector<double> text_offset;
cr.select_font_face(_attrs.template get<string>(VERTEX_FONT_FAMILY), cr.select_font_face(_attrs.template get<string>(VERTEX_FONT_FAMILY),
static_cast<Cairo::FontSlant>(_attrs.template get<int32_t>(VERTEX_FONT_SLANT)), static_cast<Cairo::FontSlant>(_attrs.template get<int32_t>(VERTEX_FONT_SLANT)),
static_cast<Cairo::FontWeight>(_attrs.template get<int32_t>(VERTEX_FONT_WEIGHT))); static_cast<Cairo::FontWeight>(_attrs.template get<int32_t>(VERTEX_FONT_WEIGHT)));
cr.set_font_size(get_user_dist(cr, _attrs.template get<double>(VERTEX_FONT_SIZE))); cr.set_font_size(get_user_dist(cr, _attrs.template get<double>(VERTEX_FONT_SIZE)));
text_pos = _attrs.template get<double>(VERTEX_TEXT_POSITION); text_pos = _attrs.template get<double>(VERTEX_TEXT_POSITION);
text_rotation = _attrs.template get<double>(VERTEX_TEXT_ROTATION); text_rotation = _attrs.template get<double>(VERTEX_TEXT_ROTATION);
text_offset = _attrs.template get<vector<double> >(VERTEX_TEXT_OFFSET); text_offset = _attrs.template get<vector<double> >(VERTEX_TEXT_OFFSET);
text_offset.resize(2, 0.0); text_offset.resize(2, 0.0);
cr.rotate(text_rotation); cr.rotate(text_rotation);
Cairo::TextExtents extents; Cairo::TextExtents extents;
cr.get_text_extents(text, extents); cr.get_text_extents(text, extents);
Cairo::FontExtents fextents; Cairo::FontExtents fextents;
cr.get_font_extents(fextents); cr.get_font_extents(fextents);
if (text_pos < 0) if (text_pos < 0)
{ {
cr.translate(-extents.width / 2 - extents.x_bearing + text_offset[0], cr.translate(-extents.width / 2 - extents.x_bearing + text_offset[0],
extents.height / 2 + text_offset[1]); extents.height / 2 + text_offset[1]);
} }
else else
{ {
pos_t origin; pos_t origin;
origin.first = _pos.first + size * cos(text_pos); origin.first = _pos.first + size * cos(text_pos);
origin.second = _pos.second + size * sin(text_pos); origin.second = _pos.second + size * sin(text_pos);
pos_t anchor = get_anchor(origin, cr, true); pos_t anchor = get_anchor(origin, cr, true);
double angle = atan2(_pos.second - anchor.second, double angle = atan2(_pos.second - anchor.second,
_pos.first - anchor.first) + M_PI; _pos.first - anchor.first) + M_PI;
anchor.first = size * 1.2 * cos(angle); anchor.first = size * 1.2 * cos(angle);
anchor.second = size * 1.2 * sin(angle); anchor.second = size * 1.2 * sin(angle);
anchor.first += text_offset[0]; anchor.first += text_offset[0];
anchor.second += text_offset[1] + extents.height / 2; anchor.second += text_offset[1] + extents.height / 2;
if (anchor.first < 0) if (anchor.first < 0)
anchor.first -= extents.width; anchor.first -= extents.width;
cr.translate(anchor.first, anchor.second); cr.translate(anchor.first, anchor.second);
}
color = _attrs.template get<color_t>(VERTEX_TEXT_COLOR);
cr.set_source_rgba(get<0>(color), get<1>(color), get<2>(color),
get<3>(color));
cr.show_text(text);
cr.begin_new_path();
cr.restore();
} }
color = _attrs.template get<color_t>(VERTEX_TEXT_COLOR);
cr.set_source_rgba(get<0>(color), get<1>(color), get<2>(color),
get<3>(color));
cr.show_text(text);
cr.begin_new_path();
cr.restore();
}
if (!outline)
cr.restore(); cr.restore();
}
else else
{
cr.translate(-_pos.first, -_pos.second); cr.translate(-_pos.first, -_pos.second);
}
} }
template <class, class> template <class, class>
...@@ -1045,14 +1071,10 @@ public: ...@@ -1045,14 +1071,10 @@ public:
bool has_gradient = gradient.size() >= 2; bool has_gradient = gradient.size() >= 2;
edge_marker_t start_marker = _attrs.template get<edge_marker_t>(EDGE_START_MARKER); edge_marker_t start_marker = _attrs.template get<edge_marker_t>(EDGE_START_MARKER);
edge_marker_t mid_marker = _attrs.template get<edge_marker_t>(EDGE_MID_MARKER);
edge_marker_t end_marker = _attrs.template get<edge_marker_t>(EDGE_END_MARKER); edge_marker_t end_marker = _attrs.template get<edge_marker_t>(EDGE_END_MARKER);
double marker_size = _attrs.template get<double>(EDGE_MARKER_SIZE); double marker_size = _attrs.template get<double>(EDGE_MARKER_SIZE);
marker_size = get_user_dist(cr, marker_size); marker_size = get_user_dist(cr, marker_size);
bool sloppy = _attrs.template get<uint8_t>(EDGE_SLOPPY);
if (_s.get_size(cr) < get_user_dist(cr, res) &&
_t.get_size(cr) < get_user_dist(cr, res))
sloppy = true;
pos_begin = _s.get_pos(); pos_begin = _s.get_pos();
pos_end = _t.get_pos(); pos_end = _t.get_pos();
...@@ -1124,20 +1146,27 @@ public: ...@@ -1124,20 +1146,27 @@ public:
pos_end_marker = _t.get_anchor(pos_begin, cr); pos_end_marker = _t.get_anchor(pos_begin, cr);
} }
double a = get<3>(color);
a *= get<3>(_s._attrs.template get<color_t>(VERTEX_COLOR));
a *= get<3>(_s._attrs.template get<color_t>(VERTEX_FILL_COLOR));
a *= get<3>(_t._attrs.template get<color_t>(VERTEX_COLOR));
a *= get<3>(_t._attrs.template get<color_t>(VERTEX_FILL_COLOR));
if (!sloppy && a < 1) bool sloppy = _attrs.template get<uint8_t>(EDGE_SLOPPY);
if (_s.get_size(cr) < get_user_dist(cr, res) &&
_t.get_size(cr) < get_user_dist(cr, res))
sloppy = true;
bool seamless = _attrs.template get<uint8_t>(EDGE_SEAMLESS);
if (marker_size < get_user_dist(cr, res) ||
(start_marker == MARKER_SHAPE_NONE &&
mid_marker == MARKER_SHAPE_NONE &&
end_marker == MARKER_SHAPE_NONE))
seamless = false;
if (seamless)
{ {
// set the clip region to the correct size for better push/pop_group // set the clip region to the correct size for better push/pop_group
// performance // performance
double sx1, sy1, sx2, sy2;
draw_edge_markers(pos_begin_marker, pos_begin_d, pos_end_marker, draw_edge_markers(pos_begin_marker, pos_begin_d, pos_end_marker,
pos_end_d, controls, marker_size, cr); pos_end_d, controls, marker_size, cr);
draw_edge_line(pos_begin, pos_end, controls, cr); draw_edge_line(pos_begin, pos_end, controls, cr);
double sx1, sy1, sx2, sy2;
cr.get_stroke_extents(sx1, sy1, sx2, sy2); cr.get_stroke_extents(sx1, sy1, sx2, sy2);
cr.begin_new_path(); cr.begin_new_path();
cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1); cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1);
...@@ -1202,9 +1231,48 @@ public: ...@@ -1202,9 +1231,48 @@ public:
} }
else else
{ {
if (!sloppy)
{
// compose clip region
double sx1, sy1, sx2, sy2;
draw_edge_line(pos_begin, pos_end, controls, cr);
cr.get_stroke_extents(sx1, sy1, sx2, sy2);
cr.begin_new_path();
cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1);
draw_edge_markers(pos_begin_marker, pos_begin_d, pos_end_marker,
pos_end_d, controls, marker_size, cr);
cr.set_fill_rule(Cairo::FILL_RULE_EVEN_ODD);
cr.clip();
cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1);
_s.draw(cr, true);
cr.clip();
cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1);
_t.draw(cr, true);
cr.clip();
if (start_marker != MARKER_SHAPE_NONE && start_marker != MARKER_SHAPE_BAR)
{
cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1);
cr.arc(pos_begin_marker.first, pos_begin_marker.second,
marker_size / 2, 0, 2 * M_PI);
cr.clip();
}
if (end_marker != MARKER_SHAPE_NONE && end_marker != MARKER_SHAPE_BAR)
{
cr.rectangle(sx1, sy1, sx2 - sx1, sy2 - sy1);
cr.arc(pos_end_marker.first, pos_end_marker.second,
marker_size / 2, 0, 2 * M_PI);
cr.clip();
}
}
if (!has_gradient) if (!has_gradient)
{ {
cr.set_source_rgba(get<0>(color), get<1>(color), get<2>(color), get<3>(color)); cr.set_source_rgba(get<0>(color), get<1>(color), get<2>(color),
get<3>(color));
} }
else else
{ {
...@@ -1223,14 +1291,13 @@ public: ...@@ -1223,14 +1291,13 @@ public:
} }
cr.set_source(gd); cr.set_source(gd);
} }
if (marker_size > get_user_dist(cr, res))
{
draw_edge_markers(pos_begin_marker, pos_begin_d, pos_end_marker,
pos_end_d, controls, marker_size, cr);
cr.fill();
}
draw_edge_line(pos_begin, pos_end, controls, cr); draw_edge_line(pos_begin, pos_end, controls, cr);
cr.stroke(); cr.stroke();
cr.reset_clip();
draw_edge_markers(pos_begin_marker, pos_begin_d, pos_end_marker,
pos_end_d, controls, marker_size, cr);
cr.fill();
} }
string text = _attrs.template get<string>(EDGE_TEXT); string text = _attrs.template get<string>(EDGE_TEXT);
...@@ -2145,7 +2212,8 @@ BOOST_PYTHON_MODULE(libgraph_tool_draw) ...@@ -2145,7 +2212,8 @@ BOOST_PYTHON_MODULE(libgraph_tool_draw)
.value("font_slant", EDGE_FONT_SLANT) .value("font_slant", EDGE_FONT_SLANT)
.value("font_weight", EDGE_FONT_WEIGHT) .value("font_weight", EDGE_FONT_WEIGHT)
.value("font_size", EDGE_FONT_SIZE) .value("font_size", EDGE_FONT_SIZE)
.value("sloppy", EDGE_SLOPPY); .value("sloppy", EDGE_SLOPPY)
.value("seamless", EDGE_SEAMLESS);
enum_<vertex_shape_t>("vertex_shape") enum_<vertex_shape_t>("vertex_shape")
.value("circle", SHAPE_CIRCLE) .value("circle", SHAPE_CIRCLE)
......
...@@ -144,7 +144,8 @@ _edefaults = { ...@@ -144,7 +144,8 @@ _edefaults = {
"font_slant": cairo.FONT_SLANT_NORMAL, "font_slant": cairo.FONT_SLANT_NORMAL,
"font_weight": cairo.FONT_WEIGHT_NORMAL, "font_weight": cairo.FONT_WEIGHT_NORMAL,
"font_size": 12., "font_size": 12.,
"sloppy": False "sloppy": False,
"seamless": False
} }
......
...@@ -477,11 +477,11 @@ class GraphWidget(Gtk.DrawingArea): ...@@ -477,11 +477,11 @@ class GraphWidget(Gtk.DrawingArea):
cr.paint() cr.paint()
cr.set_matrix(self.tmatrix) cr.set_matrix(self.tmatrix)
mtime = -1 if complete else self.regenerate_max_time mtime = -1 if complete else self.regenerate_max_time
res = 5 * self.get_scale_factor()
count = cairo_draw(self.g, self.pos, cr, self.vprops, self.eprops, count = cairo_draw(self.g, self.pos, cr, self.vprops, self.eprops,
self.vorder, self.eorder, self.nodesfirst, res=1, self.vorder, self.eorder, self.nodesfirst, res=res,
render_offset=self.regenerate_offset, render_offset=self.regenerate_offset,
max_render_time=mtime, max_render_time=mtime, **self.kwargs)
**self.kwargs)
self.regenerate_offset = count self.regenerate_offset = count
def draw(self, da, cr): def draw(self, da, cr):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment