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