Graphviz 13.0.0~dev.20241220.2304
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 <common/utils.h>
25#include <common/color.h>
26#include <util/agxbuf.h>
27#include <util/prisize_t.h>
28#include <util/streq.h>
29#include <util/unreachable.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 const size_t span_length = strlen(span->str);
227 const double length = 2.0 * font_size / 3.0 * (double)span_length / 2.0;
228
229 pA = span->font->postscript_alias;
230 if (pA) /* if it is a standard postscript font */
231 font = pA->xfig_code;
232
233 switch (span->just) {
234 case 'l':
235 sub_type = 0;
236 break;
237 case 'r':
238 sub_type = 2;
239 break;
240 default:
241 case 'n':
242 sub_type = 1;
243 break;
244 }
245
246/* object_code sub_type color depth pen_style font
247 4 1 0 1 0 0
248 font_size angle font_flags height length ROUND(p.x) ROUND(p.y),
249 14.0 0.0000 6 14.0 51.3 1237 570
250 $A \\in M_0$\001
251*/
252 gvprintf(job,
253 "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %.0f %.0f ",
254 object_code, sub_type, color, depth, pen_style, font,
255 font_size, angle, font_flags, height, length, round(p.x),
256 round(p.y - 72.0));
257 gvputs_nonascii(job, span->str);
258 gvputs(job, "\\001\n");
259}
260
261static void fig_ellipse(GVJ_t * job, pointf * A, int filled)
262{
263 obj_state_t *obj = job->obj;
264
265 int object_code = 1; /* always 1 for ellipse */
266 int sub_type = 1; /* ellipse defined by radii */
267 int line_style; /* solid, dotted, dashed */
268 double thickness = round(obj->penwidth);
269 int pen_color = obj->pencolor.u.index;
270 int fill_color = obj->fillcolor.u.index;
271 int depth = Depth;
272 int pen_style = 0; /* not used */
273 int area_fill = filled ? 20 : -1;
274 double style_val;
275 int direction = 0;
276 double angle = 0.0;
277 double center_x, center_y;
278
279 fig_line_style(obj, &line_style, &style_val);
280
281 const double start_x = center_x = round(A[0].x);
282 const double start_y = center_y = round(A[0].y);
283 const double radius_x = round(A[1].x - A[0].x);
284 const double radius_y = round(A[1].y - A[0].y);
285 const double end_x = round(A[1].x);
286 const double end_y = round(A[1].y);
287
288 gvprintf(job,
289 "%d %d %d %.0f %d %d %d %d %d %.3f %d %.4f %.0f %.0f %.0f %.0f "
290 "%.0f %.0f %.0f %.0f\n",
291 object_code, sub_type, line_style, thickness, pen_color,
292 fill_color, depth, pen_style, area_fill, style_val, direction,
293 angle, center_x, center_y, radius_x, radius_y, start_x,
294 start_y, end_x, end_y);
295}
296
297static void fig_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
298 obj_state_t *obj = job->obj;
299
300 int object_code = 3; /* always 3 for spline */
301 int sub_type;
302 int line_style; /* solid, dotted, dashed */
303 double thickness = round(obj->penwidth);
304 int pen_color = obj->pencolor.u.index;
305 int fill_color = obj->fillcolor.u.index;
306 int depth = Depth;
307 int pen_style = 0; /* not used */
308 int area_fill;
309 double style_val;
310 int cap_style = 0;
311 int forward_arrow = 0;
312 int backward_arrow = 0;
313
314 pointf pf, V[4];
315 int step;
316 int count = 0;
317
318 agxbuf buf = {0};
319 assert (n >= 4);
320
321 fig_line_style(obj, &line_style, &style_val);
322
323 if (filled) {
324 sub_type = 5; /* closed X-spline */
325 area_fill = 20; /* fully saturated color */
326 fill_color = job->obj->fillcolor.u.index;
327 }
328 else {
329 sub_type = 4; /* opened X-spline */
330 area_fill = -1;
331 fill_color = 0;
332 }
333 V[3].x = A[0].x;
334 V[3].y = A[0].y;
335 /* Write first point in line */
336 count++;
337 agxbprint(&buf, " %.0f %.0f", A[0].x, A[0].y);
338 /* write subsequent points */
339 for (size_t i = 0; i + 3 < n; i += 3) {
340 V[0] = V[3];
341 for (size_t j = 1; j <= 3; j++) {
342 V[j].x = A[i + j].x;
343 V[j].y = A[i + j].y;
344 }
345 for (step = 1; step <= BEZIERSUBDIVISION; step++) {
346 count++;
347 pf = Bezier(V, (double)step / BEZIERSUBDIVISION, NULL, NULL);
348 agxbprint(&buf, " %.0f %.0f", pf.x, pf.y);
349 }
350 }
351
352 gvprintf(job, "%d %d %d %.0f %d %d %d %d %d %.1f %d %d %d %d\n",
353 object_code,
354 sub_type,
355 line_style,
356 thickness,
357 pen_color,
358 fill_color,
359 depth,
360 pen_style,
361 area_fill,
362 style_val, cap_style, forward_arrow, backward_arrow, count);
363
364 gvprintf(job, " %s\n", agxbuse(&buf)); /* print points */
365 agxbfree(&buf);
366 for (int i = 0; i < count; i++) {
367 gvprintf(job, " %d", i % (count - 1) ? 1 : 0); /* -1 on all */
368 }
369 gvputs(job, "\n");
370}
371
372static void fig_polygon(GVJ_t *job, pointf *A, size_t n, int filled) {
373 obj_state_t *obj = job->obj;
374
375 int object_code = 2; /* always 2 for polyline */
376 int sub_type = 3; /* always 3 for polygon */
377 int line_style; /* solid, dotted, dashed */
378 double thickness = round(obj->penwidth);
379 int pen_color = obj->pencolor.u.index;
380 int fill_color = obj->fillcolor.u.index;
381 int depth = Depth;
382 int pen_style = 0; /* not used */
383 int area_fill = filled ? 20 : -1;
384 double style_val;
385 int join_style = 0;
386 int cap_style = 0;
387 int radius = 0;
388 int forward_arrow = 0;
389 int backward_arrow = 0;
390 const size_t npoints = n + 1;
391
392 fig_line_style(obj, &line_style, &style_val);
393
394 gvprintf(job,
395 "%d %d %d %.0f %d %d %d %d %d %.1f %d %d %d %d %d %" PRISIZE_T "\n",
396 object_code, sub_type, line_style, thickness, pen_color,
397 fill_color, depth, pen_style, area_fill, style_val, join_style,
398 cap_style, radius, forward_arrow, backward_arrow, npoints);
399 figptarray(job, A, n, 1); // closed shape
400}
401
402static void fig_polyline(GVJ_t *job, pointf *A, size_t n) {
403 obj_state_t *obj = job->obj;
404
405 int object_code = 2; /* always 2 for polyline */
406 int sub_type = 1; /* always 1 for polyline */
407 int line_style; /* solid, dotted, dashed */
408 double thickness = round(obj->penwidth);
409 int pen_color = obj->pencolor.u.index;
410 int fill_color = 0;
411 int depth = Depth;
412 int pen_style = 0; /* not used */
413 int area_fill = 0;
414 double style_val;
415 int join_style = 0;
416 int cap_style = 0;
417 int radius = 0;
418 int forward_arrow = 0;
419 int backward_arrow = 0;
420 const size_t npoints = n;
421
422 fig_line_style(obj, &line_style, &style_val);
423
424 gvprintf(job,
425 "%d %d %d %.0f %d %d %d %d %d %.1f %d %d %d %d %d %" PRISIZE_T "\n",
426 object_code, sub_type, line_style, thickness, pen_color,
427 fill_color, depth, pen_style, area_fill, style_val, join_style,
428 cap_style, radius, forward_arrow, backward_arrow, npoints);
429 figptarray(job, A, n, 0); // open shape
430}
431
433 0, /* fig_begin_job */
434 0, /* fig_end_job */
437 0, /* fig_begin_layer */
438 0, /* fig_end_layer */
440 0, /* fig_end_page */
441 0, /* fig_begin_cluster */
442 0, /* fig_end_cluster */
443 0, /* fig_begin_nodes */
444 0, /* fig_end_nodes */
445 0, /* fig_begin_edges */
446 0, /* fig_end_edges */
451 0, /* fig_begin_anchor */
452 0, /* fig_end_anchor */
453 0, /* fig_begin_label */
454 0, /* fig_end_label */
462 0, /* fig_library_shape */
463};
464
465
466/* NB. List must be LANG_C sorted */
467static char *fig_knowncolors[] = {
468 "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow",
469};
470
471
474 | GVRENDER_Y_GOES_DOWN, /* flags */
475 4., /* default pad - graph units */
476 fig_knowncolors, /* knowncolors */
477 sizeof(fig_knowncolors) / sizeof(char *), /* sizeof knowncolors */
478 RGBA_BYTE, /* color_type */
479};
480
483 | GVRENDER_Y_GOES_DOWN, /* flags */
484 {0.,0.}, /* default margin - points */
485 {0.,0.}, /* default page width, height - points */
486 {1440.,1440.}, /* default dpi */
487 /* FIXME - this default dpi is a very strange number!!!
488 * It was picked to make .png usershapes the right size on my screen.
489 * It happens to be 1.2 * 1200, but I can't explain the 1.2.
490 * (I was expecting 1.3333 which is 96/72, but thats too big.)
491 * Also 1200 is hardcoded in fig_begin_graph() instead of using job->dpi
492 */
493
494 /* It may be TWIPS, i.e. 20 * POINT_PER_INCH
495 * but that doesn't explain what the 1200 is? */
496};
497
502
504 {FORMAT_FIG, "fig:fig", 1, NULL, &device_features_fig},
505 {0, NULL, 0, NULL, NULL}
506};
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
#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:168
#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:163
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:129
void gvputs_nonascii(GVJ_t *job, const char *s)
Definition gvdevice.c:279
int gvputs(GVJ_t *job, const char *s)
Definition gvdevice.c:264
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:395
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)
textitem scanner parser str
Definition htmlparse.y:224
$2 font
Definition htmlparse.y:300
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:73
#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::@71 u
ingroup plugin_api
Definition gvplugin.h:35
graph_t * g
Definition gvcjob.h:186
gvcolor_t fillcolor
Definition gvcjob.h:194
union obj_state_s::@89 u
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