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
36NOEXPORT SkillGuiGraphViewport *sggvp_ = NULL;
37
38NOEXPORT std::valarray<double> skillgui_render_dashed_(6., 1);
39NOEXPORT std::valarray<double> skillgui_render_dotted_((double[]){2., 6.}, 2);
40
41#ifdef USE_GVPLUGIN_TIMETRACKER
42NOEXPORT fawkes::TimeTracker tt_;
43NOEXPORT unsigned int ttc_page_ = tt_.add_class("Page");
44NOEXPORT unsigned int ttc_beginpage_ = tt_.add_class("Begin Page");
45NOEXPORT unsigned int ttc_ellipse_ = tt_.add_class("Ellipse");
46NOEXPORT unsigned int ttc_bezier_ = tt_.add_class("Bezier");
47NOEXPORT unsigned int ttc_polygon_ = tt_.add_class("Polygon");
48NOEXPORT unsigned int ttc_polyline_ = tt_.add_class("Polyline");
49NOEXPORT unsigned int ttc_text_ = tt_.add_class("Text");
50NOEXPORT unsigned int ttc_text_1_ = tt_.add_class("Text 1");
51NOEXPORT unsigned int ttc_text_2_ = tt_.add_class("Text 2");
52NOEXPORT unsigned int ttc_text_3_ = tt_.add_class("Text 3");
53NOEXPORT unsigned int ttc_text_4_ = tt_.add_class("Text 4");
54NOEXPORT unsigned int ttc_text_5_ = tt_.add_class("Text 5");
55NOEXPORT unsigned int tt_count_ = 0;
56NOEXPORT unsigned int num_ellipse_ = 0;
57NOEXPORT unsigned int num_bezier_ = 0;
58NOEXPORT unsigned int num_polygon_ = 0;
59NOEXPORT unsigned int num_polyline_ = 0;
60NOEXPORT unsigned int num_text_ = 0;
61#endif
62
63static void
64skillgui_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
78static void
79skillgui_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
90static inline Papyrus::Fill::pointer
91skillgui_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
101static inline Papyrus::Stroke::pointer
102skillgui_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
123static void
124skillgui_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
183static void
184skillgui_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
208static void
209skillgui_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
290static void
291skillgui_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
317static void
318skillgui_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
343static void
344skillgui_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
381static void
382skillgui_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
405static 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
438static gvdevice_engine_t skillgui_device_engine = {
439 skillgui_device_init,
440 NULL, /* skillgui_device_format */
441 skillgui_device_finalize,
442};
443
444#ifdef __cplusplus
445extern "C" {
446#endif
447
448static 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
457static 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
464gvplugin_installed_t gvdevice_types_skillgui[] = {
465 {0, (char *)"skillgui:skillgui", 0, &skillgui_device_engine, &skillgui_device_features},
466 {0, NULL, 0, NULL, NULL}};
467
468gvplugin_installed_t gvrender_types_skillgui[] = {
469 {0, (char *)"skillgui", 10, &skillgui_render_engine, &skillgui_render_features},
470 {0, NULL, 0, NULL, NULL}};
471
472static gvplugin_api_t apis[] = {
473 {API_device, gvdevice_types_skillgui},
474 {API_render, gvrender_types_skillgui},
475 {(api_t)0, 0},
476};
477
478gvplugin_library_t gvplugin_skillgui_LTX_library = {(char *)"skillgui", apis};
479
480#ifdef __cplusplus
481}
482#endif
483
484void
485gvplugin_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