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