Fawkes API  Fawkes Development Version
gvplugin_skillgui_papyrus.cpp
1 
2 /***************************************************************************
3  * gvplugin_skillgui_papyrus.cpp - Graphviz plugin for Skill GUI using
4  * the Cairo-based Papyrus scene graph lib
5  *
6  * Created: Tue Dec 16 14:36:51 2008
7  * Copyright 2008-2009 Tim Niemueller [www.niemueller.de]
8  *
9  ****************************************************************************/
10 
11 /* This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL file in the doc directory.
22  */
23 
24 #include "gvplugin_skillgui_papyrus.h"
25 
26 #include <utils/math/angle.h>
27 #include <utils/time/tracker.h>
28 
29 #include <algorithm>
30 #include <cstdio>
31 #include <gvplugin_device.h>
32 #include <gvplugin_render.h>
33 
34 #define NOEXPORT __attribute__((visibility("hidden")))
35 
36 NOEXPORT SkillGuiGraphViewport *sggvp_ = NULL;
37 
38 NOEXPORT std::valarray<double> skillgui_render_dashed_(6., 1);
39 NOEXPORT std::valarray<double> skillgui_render_dotted_((double[]){2., 6.}, 2);
40 
41 #ifdef USE_GVPLUGIN_TIMETRACKER
42 NOEXPORT fawkes::TimeTracker tt_;
43 NOEXPORT unsigned int ttc_page_ = tt_.add_class("Page");
44 NOEXPORT unsigned int ttc_beginpage_ = tt_.add_class("Begin Page");
45 NOEXPORT unsigned int ttc_ellipse_ = tt_.add_class("Ellipse");
46 NOEXPORT unsigned int ttc_bezier_ = tt_.add_class("Bezier");
47 NOEXPORT unsigned int ttc_polygon_ = tt_.add_class("Polygon");
48 NOEXPORT unsigned int ttc_polyline_ = tt_.add_class("Polyline");
49 NOEXPORT unsigned int ttc_text_ = tt_.add_class("Text");
50 NOEXPORT unsigned int ttc_text_1_ = tt_.add_class("Text 1");
51 NOEXPORT unsigned int ttc_text_2_ = tt_.add_class("Text 2");
52 NOEXPORT unsigned int ttc_text_3_ = tt_.add_class("Text 3");
53 NOEXPORT unsigned int ttc_text_4_ = tt_.add_class("Text 4");
54 NOEXPORT unsigned int ttc_text_5_ = tt_.add_class("Text 5");
55 NOEXPORT unsigned int tt_count_ = 0;
56 NOEXPORT unsigned int num_ellipse_ = 0;
57 NOEXPORT unsigned int num_bezier_ = 0;
58 NOEXPORT unsigned int num_polygon_ = 0;
59 NOEXPORT unsigned int num_polyline_ = 0;
60 NOEXPORT unsigned int num_text_ = 0;
61 #endif
62 
63 static void
64 skillgui_device_init(GVJ_t *firstjob)
65 {
66  Glib::RefPtr<const Gdk::Screen> s = sggvp_->get_screen();
67  firstjob->device_dpi.x = s->get_resolution();
68  firstjob->device_dpi.y = s->get_resolution();
69  firstjob->device_sets_dpi = true;
70 
71  Gtk::Allocation alloc = sggvp_->get_allocation();
72  firstjob->width = alloc.get_width();
73  firstjob->height = alloc.get_height();
74 
75  firstjob->fit_mode = TRUE;
76 }
77 
78 static void
79 skillgui_device_finalize(GVJ_t *firstjob)
80 {
81  sggvp_->set_gvjob(firstjob);
82 
83  firstjob->context = (void *)sggvp_;
84  firstjob->external_context = TRUE;
85 
86  // Render!
87  (firstjob->callbacks->refresh)(firstjob);
88 }
89 
90 static inline Papyrus::Fill::pointer
91 skillgui_render_solidpattern(gvcolor_t *color)
92 {
93  Cairo::RefPtr<Cairo::SolidPattern> pattern;
94  pattern = Cairo::SolidPattern::create_rgba(color->u.RGBA[0],
95  color->u.RGBA[1],
96  color->u.RGBA[2],
97  color->u.RGBA[3]);
98  return Papyrus::Fill::create(pattern);
99 }
100 
101 static inline Papyrus::Stroke::pointer
102 skillgui_render_stroke(obj_state_t *obj)
103 {
104  Papyrus::Stroke::pointer stroke;
105 
106  Cairo::RefPtr<Cairo::SolidPattern> pattern;
107  pattern = Cairo::SolidPattern::create_rgba(obj->pencolor.u.RGBA[0],
108  obj->pencolor.u.RGBA[1],
109  obj->pencolor.u.RGBA[2],
110  obj->pencolor.u.RGBA[3]);
111 
112  stroke = Papyrus::Stroke::create(pattern, obj->penwidth);
113 
114  if (obj->pen == PEN_DASHED) {
115  stroke->set_dash(skillgui_render_dashed_);
116  } else if (obj->pen == PEN_DOTTED) {
117  stroke->set_dash(skillgui_render_dotted_);
118  }
119 
120  return stroke;
121 }
122 
123 static void
124 skillgui_render_begin_page(GVJ_t *job)
125 {
126 #ifdef USE_GVPLUGIN_TIMETRACKER
127  tt_.ping_start(ttc_page_);
128  tt_.ping_start(ttc_beginpage_);
129 #endif
130  SkillGuiGraphViewport *gvp = static_cast<SkillGuiGraphViewport *>(job->context);
131  gvp->clear();
132  Gtk::Allocation alloc = sggvp_->get_allocation();
133  float bbwidth = job->bb.UR.x - job->bb.LL.x;
134  float bbheight = job->bb.UR.y - job->bb.LL.y;
135  float avwidth = alloc.get_width();
136  float avheight = alloc.get_height();
137  float zoom_w = avwidth / bbwidth;
138  float zoom_h = avheight / bbheight;
139  float zoom = std::min(zoom_w, zoom_h);
140 
141  float translate_x = 0;
142  float translate_y = 0;
143 
144  if (bbwidth > avwidth || bbheight > avheight) {
145  float zwidth = bbwidth * zoom;
146  float zheight = bbheight * zoom;
147  translate_x += (avwidth - zwidth) / 2.;
148  translate_y += (avheight - zheight) / 2.;
149  } else {
150  zoom = 1.0;
151  translate_x += (avwidth - bbwidth) / 2.;
152  translate_y += (avheight - bbheight) / 2.;
153  }
154 
155  gvp->set_bb(bbwidth, bbheight);
156  gvp->set_pad(job->pad.x, job->pad.y);
157  gvp->set_scale(zoom);
158  gvp->set_translation(translate_x, translate_y);
159 
160  if (!gvp->scale_override()) {
161  gvp->get_affine()->set_translate(translate_x + job->pad.x, translate_y + job->pad.y);
162  gvp->get_affine()->set_scale(zoom);
163  }
164 
165  /*
166  char *graph_translate_x = agget(obj->u.g, (char *)"trans_x");
167  if (graph_translate_x && (strlen(graph_translate_x) > 0)) {
168  float translate_x = atof(graph_translate_x) * zoom;
169  float translate_y = atof(graph_translate_y) * job->scale.x;
170  }
171  */
172 #ifdef USE_GVPLUGIN_TIMETRACKER
173  num_ellipse_ = 0;
174  num_bezier_ = 0;
175  num_polygon_ = 0;
176  num_polyline_ = 0;
177  num_text_ = 0;
178 
179  tt_.ping_end(ttc_beginpage_);
180 #endif
181 }
182 
183 static void
184 skillgui_render_end_page(GVJ_t *job)
185 {
186  //SkillGuiGraphViewport *gvp = (SkillGuiGraphViewport *)job->context;
187  //gvp->queue_draw();
188 #ifdef USE_GVPLUGIN_TIMETRACKER
189  tt_.ping_end(ttc_page_);
190  if (++tt_count_ >= 10) {
191  tt_count_ = 0;
192  tt_.print_to_stdout();
193 
194  printf("Num Ellipse: %u\n"
195  "Num Bezier: %u\n"
196  "Num Polygon: %u\n"
197  "Num Polyline: %u\n"
198  "Num Text: %u\n",
199  num_ellipse_,
200  num_bezier_,
201  num_polygon_,
202  num_polyline_,
203  num_text_);
204  }
205 #endif
206 }
207 
208 static void
209 skillgui_render_textpara(GVJ_t *job, pointf p, textpara_t *para)
210 {
211 #ifdef USE_GVPLUGIN_TIMETRACKER
212  tt_.ping_start(ttc_text_);
213  ++num_text_;
214 #endif
215  SkillGuiGraphViewport *gvp = (SkillGuiGraphViewport *)job->context;
216  obj_state_t * obj = job->obj;
217 
218  switch (para->just) {
219  case 'r': p.x -= para->width; break;
220  case 'l': p.x -= 0.0; break;
221  case 'n':
222  default: p.x -= para->width / 2.0; break;
223  }
224 
225  p.y += para->height / 2.0 + para->yoffset_centerline;
226  //p.y -= para->yoffset_centerline;
227  //p.y += para->yoffset_centerline; // + para->yoffset_layout;
228 
229  Glib::RefPtr<Pango::Layout> pl = Glib::wrap((PangoLayout *)para->layout,
230  /* copy */ true);
231  Pango::FontDescription fd = pl->get_font_description();
232  Cairo::FontSlant slant = Cairo::FONT_SLANT_NORMAL;
233  if (fd.get_style() == Pango::STYLE_OBLIQUE) {
234  slant = Cairo::FONT_SLANT_OBLIQUE;
235  } else if (fd.get_style() == Pango::STYLE_ITALIC) {
236  slant = Cairo::FONT_SLANT_ITALIC;
237  }
238  Cairo::FontWeight weight = Cairo::FONT_WEIGHT_NORMAL;
239  if (fd.get_weight() == Pango::WEIGHT_BOLD) {
240  weight = Cairo::FONT_WEIGHT_BOLD;
241  }
242 
243  double offsetx = 0.0;
244  double offsety = 0.0;
245  double rotate = 0.0;
246 
247  if ((obj->type == EDGE_OBJTYPE) && (strcmp(para->str, obj->headlabel) == 0)) {
248  char *labelrotate = agget(obj->u.e, (char *)"labelrotate");
249  if (labelrotate && (strlen(labelrotate) > 0)) {
250  rotate = fawkes::deg2rad(atof(labelrotate));
251  }
252  char *labeloffsetx = agget(obj->u.e, (char *)"labeloffsetx");
253  if (labeloffsetx && (strlen(labeloffsetx) > 0)) {
254  offsetx = atof(labeloffsetx) * job->scale.x;
255  }
256  char *labeloffsety = agget(obj->u.e, (char *)"labeloffsety");
257  if (labeloffsety && (strlen(labeloffsety) > 0)) {
258  offsety = atof(labeloffsety) * job->scale.y;
259  }
260  }
261  //tt_.ping_start(ttc_text_1_);
262 
263  Papyrus::Text::pointer t =
264  Papyrus::Text::create(para->str, para->fontsize, fd.get_family(), slant, weight);
265  //t->set_stroke(skillgui_render_stroke(&(obj->pencolor)));
266  //tt_.ping_end(ttc_text_1_);
267  //tt_.ping_start(ttc_text_2_);
268 #ifdef HAVE_TIMS_PAPYRUS_PATCHES
269  t->set_fill(skillgui_render_solidpattern(&(obj->pencolor)), false);
270 #else
271  t->set_fill(skillgui_render_solidpattern(&(obj->pencolor)));
272 #endif
273  //tt_.ping_end(ttc_text_2_);
274  //tt_.ping_start(ttc_text_3_);
275  t->translate(p.x + offsetx, p.y + offsety, false);
276  //tt_.ping_end(ttc_text_3_);
277  //tt_.ping_start(ttc_text_4_);
278  if (rotate != 0.0)
279  t->set_rotation(rotate, Papyrus::RADIANS, false);
280  //tt_.ping_end(ttc_text_4_);
281  //tt_.ping_start(ttc_text_5_);
282  gvp->add_drawable(t);
283 
284  //tt_.ping_end(ttc_text_5_);
285 #ifdef USE_GVPLUGIN_TIMETRACKER
286  tt_.ping_end(ttc_text_);
287 #endif
288 }
289 
290 static void
291 skillgui_render_ellipse(GVJ_t *job, pointf *A, int filled)
292 {
293 #ifdef USE_GVPLUGIN_TIMETRACKER
294  tt_.ping_start(ttc_ellipse_);
295  ++num_ellipse_;
296 #endif
297  //printf("Render ellipse\n");
298  SkillGuiGraphViewport *gvp = (SkillGuiGraphViewport *)job->context;
299  obj_state_t * obj = job->obj;
300 
301  double rx = fabs(A[1].x - A[0].x);
302  double ry = fabs(A[1].y - A[0].y);
303 
304  Papyrus::Circle::pointer e = Papyrus::Circle::create(rx);
305  e->set_stroke(skillgui_render_stroke(obj));
306  if (filled)
307  e->set_fill(skillgui_render_solidpattern(&(obj->fillcolor)));
308  e->translate(A[0].x, A[0].y);
309  e->set_scale_y(ry / rx);
310 
311  gvp->add_drawable(e);
312 #ifdef USE_GVPLUGIN_TIMETRACKER
313  tt_.ping_end(ttc_ellipse_);
314 #endif
315 }
316 
317 static void
318 skillgui_render_polygon(GVJ_t *job, pointf *A, int n, int filled)
319 {
320 #ifdef USE_GVPLUGIN_TIMETRACKER
321  tt_.ping_start(ttc_polygon_);
322  ++num_polygon_;
323 #endif
324  //printf("Polygon\n");
325  SkillGuiGraphViewport *gvp = (SkillGuiGraphViewport *)job->context;
326  obj_state_t * obj = job->obj;
327 
328  Papyrus::Vertices v;
329  for (int i = 0; i < n; ++i) {
330  v.push_back(Papyrus::Vertex(A[i].x, A[i].y));
331  }
332 
333  Papyrus::Polygon::pointer p = Papyrus::Polygon::create(v);
334  p->set_stroke(skillgui_render_stroke(obj));
335  if (filled)
336  p->set_fill(skillgui_render_solidpattern(&(obj->fillcolor)));
337  gvp->add_drawable(p);
338 #ifdef USE_GVPLUGIN_TIMETRACKER
339  tt_.ping_end(ttc_polygon_);
340 #endif
341 }
342 
343 static void
344 skillgui_render_bezier(GVJ_t * job,
345  pointf *A,
346  int n,
347  int arrow_at_start,
348  int arrow_at_end,
349  int filled)
350 {
351 #ifdef USE_GVPLUGIN_TIMETRACKER
352  tt_.ping_start(ttc_bezier_);
353  ++num_bezier_;
354 #endif
355  //printf("Bezier\n");
356  SkillGuiGraphViewport *gvp = (SkillGuiGraphViewport *)job->context;
357  obj_state_t * obj = job->obj;
358 
359  Papyrus::BezierVertices v;
360  v.push_back(Papyrus::BezierVertex(A[0].x, A[0].y, A[0].x, A[0].y, A[1].x, A[1].y));
361  for (int i = 1; i < n; i += 3) {
362  if (i < (n - 4)) {
363  v.push_back(Papyrus::BezierVertex(
364  A[i + 2].x, A[i + 2].y, A[i + 1].x, A[i + 1].y, A[i + 3].x, A[i + 3].y));
365  } else {
366  v.push_back(Papyrus::BezierVertex(
367  A[i + 2].x, A[i + 2].y, A[i + 1].x, A[i + 1].y, A[i + 2].x, A[i + 2].y));
368  }
369  }
370 
371  Papyrus::Bezierline::pointer p = Papyrus::Bezierline::create(v);
372  p->set_stroke(skillgui_render_stroke(obj));
373  if (filled)
374  p->set_fill(skillgui_render_solidpattern(&(obj->fillcolor)));
375  gvp->add_drawable(p);
376 #ifdef USE_GVPLUGIN_TIMETRACKER
377  tt_.ping_end(ttc_bezier_);
378 #endif
379 }
380 
381 static void
382 skillgui_render_polyline(GVJ_t *job, pointf *A, int n)
383 {
384 #ifdef USE_GVPLUGIN_TIMETRACKER
385  tt_.ping_start(ttc_polyline_);
386  ++num_polyline_;
387 #endif
388  //printf("Polyline\n");
389  SkillGuiGraphViewport *gvp = static_cast<SkillGuiGraphViewport *>(job->context);
390  obj_state_t * obj = job->obj;
391 
392  Papyrus::Vertices v;
393  for (int i = 0; i < n; ++i) {
394  v.push_back(Papyrus::Vertex(A[i].x, A[i].y));
395  }
396 
397  Papyrus::Polyline::pointer p = Papyrus::Polyline::create(v);
398  p->set_stroke(skillgui_render_stroke(obj));
399  gvp->add_drawable(p);
400 #ifdef USE_GVPLUGIN_TIMETRACKER
401  tt_.ping_end(ttc_polyline_);
402 #endif
403 }
404 
405 static gvrender_engine_t skillgui_render_engine = {
406  0, /* skillgui_render_begin_job */
407  0, /* skillgui_render_end_job */
408  0, /* skillgui_render_begin_graph */
409  0, /* skillgui_render_end_graph */
410  0, /* skillgui_render_begin_layer */
411  0, /* skillgui_render_end_layer */
412  skillgui_render_begin_page,
413  skillgui_render_end_page,
414  0, /* skillgui_render_begin_cluster */
415  0, /* skillgui_render_end_cluster */
416  0, /* skillgui_render_begin_nodes */
417  0, /* skillgui_render_end_nodes */
418  0, /* skillgui_render_begin_edges */
419  0, /* skillgui_render_end_edges */
420  0, /* skillgui_render_begin_node */
421  0, /* skillgui_render_end_node */
422  0, /* skillgui_render_begin_edge */
423  0, /* skillgui_render_end_edge */
424  0, /* skillgui_render_begin_anchor */
425  0, /* skillgui_render_end_anchor */
426  0, /* skillgui_begin_label */
427  0, /* skillgui_end_label */
428  skillgui_render_textpara,
429  0, /* skillgui_render_resolve_color */
430  skillgui_render_ellipse,
431  skillgui_render_polygon,
432  skillgui_render_bezier,
433  skillgui_render_polyline,
434  0, /* skillgui_render_comment */
435  0, /* skillgui_render_library_shape */
436 };
437 
438 static gvdevice_engine_t skillgui_device_engine = {
439  skillgui_device_init,
440  NULL, /* skillgui_device_format */
441  skillgui_device_finalize,
442 };
443 
444 #ifdef __cplusplus
445 extern "C" {
446 #endif
447 
448 static gvrender_features_t skillgui_render_features = {
449  GVRENDER_Y_GOES_DOWN | GVRENDER_DOES_LABELS
450  | GVRENDER_DOES_TRANSFORM, /* flags, for Cairo: GVRENDER_DOES_TRANSFORM */
451  8, /* default pad - graph units */
452  0, /* knowncolors */
453  0, /* sizeof knowncolors */
454  RGBA_DOUBLE, /* color_type */
455 };
456 
457 static gvdevice_features_t skillgui_device_features = {
458  GVDEVICE_DOES_TRUECOLOR | GVDEVICE_EVENTS, /* flags */
459  {0., 0.}, /* default margin - points */
460  {0., 0.}, /* default page width, height - points */
461  {96., 96.}, /* dpi */
462 };
463 
464 gvplugin_installed_t gvdevice_types_skillgui[] = {
465  {0, (char *)"skillgui:skillgui", 0, &skillgui_device_engine, &skillgui_device_features},
466  {0, NULL, 0, NULL, NULL}};
467 
468 gvplugin_installed_t gvrender_types_skillgui[] = {
469  {0, (char *)"skillgui", 10, &skillgui_render_engine, &skillgui_render_features},
470  {0, NULL, 0, NULL, NULL}};
471 
472 static gvplugin_api_t apis[] = {
473  {API_device, gvdevice_types_skillgui},
474  {API_render, gvrender_types_skillgui},
475  {(api_t)0, 0},
476 };
477 
478 gvplugin_library_t gvplugin_skillgui_LTX_library = {(char *)"skillgui", apis};
479 
480 #ifdef __cplusplus
481 }
482 #endif
483 
484 void
485 gvplugin_skillgui_setup(GVC_t *gvc, SkillGuiGraphViewport *sggvp)
486 {
487  sggvp_ = sggvp;
488  gvAddLibrary(gvc, &gvplugin_skillgui_LTX_library);
489 }
Skill FSM Graph Viewport.
void set_gvjob(GVJ_t *job)
Set current Graphviz job.
bool scale_override()
Check if scale override is enabled.
Papyrus::AffineController::pointer get_affine()
Get scaler.
void set_pad(double pad_x, double pad_y)
Set padding.
virtual void clear()
Clear all drawables.
void set_bb(double bbw, double bbh)
Set bounding box.
void add_drawable(Papyrus::Drawable::pointer d)
Add a drawable.
void set_scale(double scale)
Set scale.
void set_translation(double tx, double ty)
Set translation.
Time tracking utility.
Definition: tracker.h:37
float deg2rad(float deg)
Convert an angle given in degrees to radians.
Definition: angle.h:36