Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
gvrender_core_fig.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12#include <assert.h>
13#include <math.h>
14#include <stdbool.h>
15#include <stdlib.h>
16#include <string.h>
17
18#include <common/macros.h>
19#include <common/const.h>
20
21#include <gvc/gvplugin_render.h>
22#include <gvc/gvplugin_device.h>
23#include <gvc/gvio.h>
24#include <cgraph/agxbuf.h>
25#include <cgraph/prisize_t.h>
26#include <cgraph/streq.h>
27#include <cgraph/unreachable.h>
28#include <common/utils.h>
29#include <common/color.h>
30
31/* Number of points to split splines into */
32#define BEZIERSUBDIVISION 6
33
34typedef enum { FORMAT_FIG, } format_type;
35
36static int Depth;
37
38static void figptarray(GVJ_t *job, pointf *A, size_t n, int close) {
39 for (size_t i = 0; i < n; i++) {
40 gvprintf(job, " %.0f %.0f", A[i].x, A[i].y);
41 }
42 if (close) {
43 gvprintf(job, " %.0f %.0f", A[0].x, A[0].y);
44 }
45 gvputs(job, "\n");
46}
47
48static int figColorResolve(bool *new, unsigned char r, unsigned char g,
49 unsigned char b)
50{
51#define maxColors 512
52 static int top = 0;
53 static short red[maxColors], green[maxColors], blue[maxColors];
54 int c;
55 int ct = -1;
56 long rd, gd, bd, dist;
57 long mindist = 3 * 255 * 255; /* init to max poss dist */
58
59 *new = false; // in case it is not a new color
60 for (c = 0; c < top; c++) {
61 rd = (long) (red[c] - r);
62 gd = (long) (green[c] - g);
63 bd = (long) (blue[c] - b);
64 dist = rd * rd + gd * gd + bd * bd;
65 if (dist < mindist) {
66 if (dist == 0)
67 return c; /* Return exact match color */
68 mindist = dist;
69 ct = c;
70 }
71 }
72 /* no exact match. We now know closest, but first try to allocate exact */
73 if (top == maxColors)
74 return ct; /* Return closest available color */
75 ++top;
76 red[c] = r;
77 green[c] = g;
78 blue[c] = b;
79 *new = true; // flag new color
80 return c; /* Return newly allocated color */
81}
82
83/* this table is in xfig color index order */
84static char *figcolor[] = {"black", "blue", "green", "cyan", "red",
85 "magenta", "yellow", "white", NULL};
86
88{
89 int object_code = 0; /* always 0 for color */
90 int i;
91
92 switch (color->type) {
93 case COLOR_STRING:
94 for (i = 0; figcolor[i]; i++) {
95 if (streq(figcolor[i], color->u.string)) {
96 color->u.index = i;
97 break;
98 }
99 }
100 break;
101 case RGBA_BYTE: {
102 bool new;
103 i = 32 + figColorResolve(&new,
104 color->u.rgba[0],
105 color->u.rgba[1],
106 color->u.rgba[2]);
107 if (new)
108 gvprintf(job, "%d %d #%02x%02x%02x\n",
109 object_code, i,
110 color->u.rgba[0],
111 color->u.rgba[1],
112 color->u.rgba[2]);
113 color->u.index = i;
114 break;
115 }
116 default:
117 UNREACHABLE(); // internal error
118 }
119
120 color->type = COLOR_INDEX;
121}
122
123static void fig_line_style(obj_state_t *obj, int *line_style, double *style_val)
124{
125 switch (obj->pen) {
126 case PEN_DASHED:
127 *line_style = 1;
128 *style_val = 10.;
129 break;
130 case PEN_DOTTED:
131 *line_style = 2;
132 *style_val = 10.;
133 break;
134 case PEN_SOLID:
135 default:
136 *line_style = 0;
137 *style_val = 0.;
138 break;
139 }
140}
141
142static void fig_comment(GVJ_t *job, char *str)
143{
144 gvprintf(job, "# %s\n", str);
145}
146
147static void fig_begin_graph(GVJ_t * job)
148{
149 obj_state_t *obj = job->obj;
150
151 gvputs(job, "#FIG 3.2\n");
152 gvprintf(job, "# Generated by %s version %s (%s)\n",
153 job->common->info[0], job->common->info[1], job->common->info[2]);
154 gvprintf(job, "# Title: %s\n", agnameof(obj->u.g));
155 gvprintf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y);
156 gvputs(job, "Portrait\n"); /* orientation */
157 gvputs(job, "Center\n"); /* justification */
158 gvputs(job, "Inches\n"); /* units */
159 gvputs(job, "Letter\n"); /* papersize */
160 gvputs(job, "100.00\n"); /* magnification % */
161 gvputs(job, "Single\n"); /* multiple-page */
162 gvputs(job, "-2\n"); /* transparent color (none) */
163 gvputs(job, "1200"); /* resolution */
164 gvputs(job, " 2\n"); /* coordinate system (upper left) */
165}
166
167static void fig_end_graph(GVJ_t * job)
168{
169 gvputs(job, "# end of FIG file\n");
170}
171
172static void fig_begin_page(GVJ_t * job)
173{
174 (void)job;
175
176 Depth = 2;
177}
178
179static void fig_begin_node(GVJ_t * job)
180{
181 (void)job;
182
183 Depth = 1;
184}
185
186static void fig_end_node(GVJ_t * job)
187{
188 (void)job;
189
190 Depth = 2;
191}
192
193static void fig_begin_edge(GVJ_t * job)
194{
195 (void)job;
196
197 Depth = 0;
198}
199
200static void fig_end_edge(GVJ_t * job)
201{
202 (void)job;
203
204 Depth = 2;
205}
206
207static void fig_textspan(GVJ_t * job, pointf p, textspan_t * span)
208{
209 obj_state_t *obj = job->obj;
210 PostscriptAlias *pA;
211
212 int object_code = 4; /* always 4 for text */
213 int sub_type = 0; /* text justification */
214 int color = obj->pencolor.u.index;
215 int depth = Depth;
216 int pen_style = 0; /* not used */
217 int font = -1; /* init to xfig's default font */
218 double font_size = span->font->size * job->zoom;
219 double angle = job->rotation ? (M_PI / 2.0) : 0.0;
220 int font_flags = 6; /* PostScript font + Special text */
221/* Special text indicates that latex markup may exist
222 * in the output - but note that dot knows nothing about latex,
223 * so the node sizes may be wrong.
224 */
225 double height = font_size;
226 double length = 2.0*font_size/3.0 * (double)strlen(span->str) / 2.0;
227
228 pA = span->font->postscript_alias;
229 if (pA) /* if it is a standard postscript font */
230 font = pA->xfig_code;
231
232 switch (span->just) {
233 case 'l':
234 sub_type = 0;
235 break;
236 case 'r':
237 sub_type = 2;
238 break;
239 default:
240 case 'n':
241 sub_type = 1;
242 break;
243 }
244
245/* object_code sub_type color depth pen_style font
246 4 1 0 1 0 0
247 font_size angle font_flags height length ROUND(p.x) ROUND(p.y),
248 14.0 0.0000 6 14.0 51.3 1237 570
249 $A \\in M_0$\001
250*/
251 gvprintf(job,
252 "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %.0f %.0f ",
253 object_code, sub_type, color, depth, pen_style, font,
254 font_size, angle, font_flags, height, length, round(p.x),
255 round(p.y - 72.0));
256 gvputs_nonascii(job, span->str);
257 gvputs(job, "\\001\n");
258}
259
260static void fig_ellipse(GVJ_t * job, pointf * A, int filled)
261{
262 obj_state_t *obj = job->obj;
263
264 int object_code = 1; /* always 1 for ellipse */
265 int sub_type = 1; /* ellipse defined by radii */
266 int line_style; /* solid, dotted, dashed */
267 double thickness = round(obj->penwidth);
268 int pen_color = obj->pencolor.u.index;
269 int fill_color = obj->fillcolor.u.index;
270 int depth = Depth;
271 int pen_style = 0; /* not used */
272 int area_fill = filled ? 20 : -1;
273 double style_val;
274 int direction = 0;
275 double angle = 0.0;
276 double center_x, center_y;
277
278 fig_line_style(obj, &line_style, &style_val);
279
280 const double start_x = center_x = round(A[0].x);
281 const double start_y = center_y = round(A[0].y);
282 const double radius_x = round(A[1].x - A[0].x);
283 const double radius_y = round(A[1].y - A[0].y);
284 const double end_x = round(A[1].x);
285 const double end_y = round(A[1].y);
286
287 gvprintf(job,
288 "%d %d %d %.0f %d %d %d %d %d %.3f %d %.4f %.0f %.0f %.0f %.0f "
289 "%.0f %.0f %.0f %.0f\n",
290 object_code, sub_type, line_style, thickness, pen_color,
291 fill_color, depth, pen_style, area_fill, style_val, direction,
292 angle, center_x, center_y, radius_x, radius_y, start_x,
293 start_y, end_x, end_y);
294}
295
296static void fig_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
297 obj_state_t *obj = job->obj;
298
299 int object_code = 3; /* always 3 for spline */
300 int sub_type;
301 int line_style; /* solid, dotted, dashed */
302 double thickness = round(obj->penwidth);
303 int pen_color = obj->pencolor.u.index;
304 int fill_color = obj->fillcolor.u.index;
305 int depth = Depth;
306 int pen_style = 0; /* not used */
307 int area_fill;
308 double style_val;
309 int cap_style = 0;
310 int forward_arrow = 0;
311 int backward_arrow = 0;
312
313 pointf pf, V[4];
314 int step;
315 int count = 0;
316
317 agxbuf buf = {0};
318 assert (n >= 4);
319
320 fig_line_style(obj, &line_style, &style_val);
321
322 if (filled) {
323 sub_type = 5; /* closed X-spline */
324 area_fill = 20; /* fully saturated color */
325 fill_color = job->obj->fillcolor.u.index;
326 }
327 else {
328 sub_type = 4; /* opened X-spline */
329 area_fill = -1;
330 fill_color = 0;
331 }
332 V[3].x = A[0].x;
333 V[3].y = A[0].y;
334 /* Write first point in line */
335 count++;
336 agxbprint(&buf, " %.0f %.0f", A[0].x, A[0].y);
337 /* write subsequent points */
338 for (size_t i = 0; i + 3 < n; i += 3) {
339 V[0] = V[3];
340 for (size_t j = 1; j <= 3; j++) {
341 V[j].x = A[i + j].x;
342 V[j].y = A[i + j].y;
343 }
344 for (step = 1; step <= BEZIERSUBDIVISION; step++) {
345 count++;
346 pf = Bezier(V, (double)step / BEZIERSUBDIVISION, NULL, NULL);
347 agxbprint(&buf, " %.0f %.0f", pf.x, pf.y);
348 }
349 }
350
351 gvprintf(job, "%d %d %d %.0f %d %d %d %d %d %.1f %d %d %d %d\n",
352 object_code,
353 sub_type,
354 line_style,
355 thickness,
356 pen_color,
357 fill_color,
358 depth,
359 pen_style,
360 area_fill,
361 style_val, cap_style, forward_arrow, backward_arrow, count);
362
363 gvprintf(job, " %s\n", agxbuse(&buf)); /* print points */
364 agxbfree(&buf);
365 for (int i = 0; i < count; i++) {
366 gvprintf(job, " %d", i % (count - 1) ? 1 : 0); /* -1 on all */
367 }
368 gvputs(job, "\n");
369}
370
371static void fig_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
372 obj_state_t *obj = job->obj;
373
374 int object_code = 2; /* always 2 for polyline */
375 int sub_type = 3; /* always 3 for polygon */
376 int line_style; /* solid, dotted, dashed */
377 double thickness = round(obj->penwidth);
378 int pen_color = obj->pencolor.u.index;
379 int fill_color = obj->fillcolor.u.index;
380 int depth = Depth;
381 int pen_style = 0; /* not used */
382 int area_fill = filled ? 20 : -1;
383 double style_val;
384 int join_style = 0;
385 int cap_style = 0;
386 int radius = 0;
387 int forward_arrow = 0;
388 int backward_arrow = 0;
389 const size_t npoints = n + 1;
390
391 fig_line_style(obj, &line_style, &style_val);
392
393 gvprintf(job,
394 "%d %d %d %.0f %d %d %d %d %d %.1f %d %d %d %d %d %" PRISIZE_T "\n",
395 object_code, sub_type, line_style, thickness, pen_color,
396 fill_color, depth, pen_style, area_fill, style_val, join_style,
397 cap_style, radius, forward_arrow, backward_arrow, npoints);
398 figptarray(job, A, n, 1); // closed shape
399}
400
401static void fig_polyline(GVJ_t *job, pointf *A, size_t n) {
402 obj_state_t *obj = job->obj;
403
404 int object_code = 2; /* always 2 for polyline */
405 int sub_type = 1; /* always 1 for polyline */
406 int line_style; /* solid, dotted, dashed */
407 double thickness = round(obj->penwidth);
408 int pen_color = obj->pencolor.u.index;
409 int fill_color = 0;
410 int depth = Depth;
411 int pen_style = 0; /* not used */
412 int area_fill = 0;
413 double style_val;
414 int join_style = 0;
415 int cap_style = 0;
416 int radius = 0;
417 int forward_arrow = 0;
418 int backward_arrow = 0;
419 const size_t npoints = n;
420
421 fig_line_style(obj, &line_style, &style_val);
422
423 gvprintf(job,
424 "%d %d %d %.0f %d %d %d %d %d %.1f %d %d %d %d %d %" PRISIZE_T "\n",
425 object_code, sub_type, line_style, thickness, pen_color,
426 fill_color, depth, pen_style, area_fill, style_val, join_style,
427 cap_style, radius, forward_arrow, backward_arrow, npoints);
428 figptarray(job, A, n, 0); // open shape
429}
430
432 0, /* fig_begin_job */
433 0, /* fig_end_job */
436 0, /* fig_begin_layer */
437 0, /* fig_end_layer */
439 0, /* fig_end_page */
440 0, /* fig_begin_cluster */
441 0, /* fig_end_cluster */
442 0, /* fig_begin_nodes */
443 0, /* fig_end_nodes */
444 0, /* fig_begin_edges */
445 0, /* fig_end_edges */
450 0, /* fig_begin_anchor */
451 0, /* fig_end_anchor */
452 0, /* fig_begin_label */
453 0, /* fig_end_label */
461 0, /* fig_library_shape */
462};
463
464
465/* NB. List must be LANG_C sorted */
466static char *fig_knowncolors[] = {
467 "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow",
468};
469
470
473 | GVRENDER_Y_GOES_DOWN, /* flags */
474 4., /* default pad - graph units */
475 fig_knowncolors, /* knowncolors */
476 sizeof(fig_knowncolors) / sizeof(char *), /* sizeof knowncolors */
477 RGBA_BYTE, /* color_type */
478};
479
482 | GVRENDER_Y_GOES_DOWN, /* flags */
483 {0.,0.}, /* default margin - points */
484 {0.,0.}, /* default page width, height - points */
485 {1440.,1440.}, /* default dpi */
486 /* FIXME - this default dpi is a very strange number!!!
487 * It was picked to make .png usershapes the right size on my screen.
488 * It happens to be 1.2 * 1200, but I can't explain the 1.2.
489 * (I was expecting 1.3333 which is 96/72, but thats too big.)
490 * Also 1200 is hardcoded in fig_begin_graph() instead of using job->dpi
491 */
492
493 /* It may be TWIPS, i.e. 20 * POINT_PER_INCH
494 * but that doesn't explain what the 1200 is? */
495};
496
501
503 {FORMAT_FIG, "fig:fig", 1, NULL, &device_features_fig},
504 {0, NULL, 0, NULL, NULL}
505};
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
#define M_PI
Definition arith.h:41
@ RGBA_BYTE
Definition color.h:26
@ COLOR_INDEX
Definition color.h:27
@ COLOR_STRING
Definition color.h:27
pointf Bezier(pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:169
#define A(n, t)
Definition expr.h:76
static double dist(int dim, double *x, double *y)
#define V
Definition gdefs.h:5
node NULL
Definition grammar.y:149
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
@ PEN_SOLID
Definition gvcjob.h:35
@ PEN_DOTTED
Definition gvcjob.h:35
@ PEN_DASHED
Definition gvcjob.h:35
#define GVRENDER_Y_GOES_DOWN
Definition gvcjob.h:94
#define EMIT_COLORS
Definition gvcjob.h:83
static void color(Agraph_t *g)
Definition gvcolor.c:128
void gvputs_nonascii(GVJ_t *job, const char *s)
Definition gvdevice.c:278
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:263
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:394
gvplugin_installed_t gvdevice_fig_types[]
static void fig_ellipse(GVJ_t *job, pointf *A, int filled)
static int figColorResolve(bool *new, unsigned char r, unsigned char g, unsigned char b)
gvrender_features_t render_features_fig
static void fig_begin_graph(GVJ_t *job)
gvdevice_features_t device_features_fig
static void fig_textspan(GVJ_t *job, pointf p, textspan_t *span)
static char * fig_knowncolors[]
static void fig_end_node(GVJ_t *job)
static void fig_begin_node(GVJ_t *job)
#define maxColors
static void fig_polygon(GVJ_t *job, pointf *A, size_t n, int filled)
gvplugin_installed_t gvrender_fig_types[]
format_type
@ FORMAT_FIG
static void fig_line_style(obj_state_t *obj, int *line_style, double *style_val)
gvrender_engine_t fig_engine
static void fig_begin_page(GVJ_t *job)
static void fig_end_graph(GVJ_t *job)
static int Depth
static void fig_end_edge(GVJ_t *job)
static void fig_comment(GVJ_t *job, char *str)
static void fig_resolve_color(GVJ_t *job, gvcolor_t *color)
static char * figcolor[]
static void fig_polyline(GVJ_t *job, pointf *A, size_t n)
static void fig_begin_edge(GVJ_t *job)
#define BEZIERSUBDIVISION
static void fig_bezier(GVJ_t *job, pointf *A, size_t n, int filled)
static void figptarray(GVJ_t *job, pointf *A, size_t n, int close)
agxbuf * str
Definition htmlparse.c:97
$2 font
Definition htmlparse.y:498
static Agedge_t * top(gv_stack_t *sp)
Definition tred.c:71
#define PRISIZE_T
PRIu64 alike for printing size_t
Definition prisize_t.h:27
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
char ** info
Definition gvcommon.h:20
int rotation
Definition gvcjob.h:319
point pagesArraySize
Definition gvcjob.h:304
obj_state_t * obj
Definition gvcjob.h:269
GVCOMMON_t * common
Definition gvcjob.h:267
double zoom
Definition gvcjob.h:318
int index
Definition color.h:37
union color_s::@72 u
ingroup plugin_api
Definition gvplugin.h:35
union obj_state_s::@92 u
graph_t * g
Definition gvcjob.h:186
gvcolor_t fillcolor
Definition gvcjob.h:194
pen_type pen
Definition gvcjob.h:197
gvcolor_t pencolor
Definition gvcjob.h:194
double penwidth
Definition gvcjob.h:199
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
PostscriptAlias * postscript_alias
Definition textspan.h:56
double size
Definition textspan.h:57
char * str
Definition textspan.h:65
char just
'l' 'n' 'r'
Definition textspan.h:71
textfont_t * font
Definition textspan.h:66
#define UNREACHABLE()
Definition unreachable.h:30
int(* pf)(void *, char *,...)
Definition xdot.c:405