1/* $Id$ $Revision$ */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include "render.h"
15#include "agxbuf.h"
16#include <stdarg.h>
17#include <string.h>
18
19#define YDIR(y) (Y_invert ? (Y_off - (y)) : (y))
20#define YFDIR(y) (Y_invert ? (YF_off - (y)) : (y))
21
22static double Y_off; /* ymin + ymax */
23static double YF_off; /* Y_off in inches */
24
25double yDir (double y)
26{
27 return YDIR(y);
28}
29
30static int (*putstr) (void *chan, const char *str);
31
32static void agputs (const char* s, FILE* fp)
33{
34 putstr ((void*)fp, s);
35}
36static void agputc (int c, FILE* fp)
37{
38 static char buf[2] = {'\0','\0'};
39 buf[0] = c;
40 putstr ((void*)fp, buf);
41}
42
43
44static void printstring(FILE * f, char *prefix, char *s)
45{
46 if (prefix) agputs(prefix, f);
47 agputs(s, f);
48}
49
50static void printint(FILE * f, char *prefix, int i)
51{
52 char buf[BUFSIZ];
53
54 if (prefix) agputs(prefix, f);
55 sprintf(buf, "%d", i);
56 agputs(buf, f);
57}
58
59static void printdouble(FILE * f, char *prefix, double v)
60{
61 char buf[BUFSIZ];
62
63 if (prefix) agputs(prefix, f);
64 sprintf(buf, "%.5g", v);
65 agputs(buf, f);
66}
67
68static void printpoint(FILE * f, pointf p)
69{
70 printdouble(f, " ", PS2INCH(p.x));
71 printdouble(f, " ", PS2INCH(YDIR(p.y)));
72}
73
74/* setYInvert:
75 * Set parameters used to flip coordinate system (y=0 at top).
76 * Values do not need to be unset, since if Y_invert is set, it's
77 * set for * all graphs during current run, so each will
78 * reinitialize the values for its bbox.
79 */
80static void setYInvert(graph_t * g)
81{
82 if (Y_invert) {
83 Y_off = GD_bb(g).UR.y + GD_bb(g).LL.y;
84 YF_off = PS2INCH(Y_off);
85 }
86}
87
88/* canon:
89 * Canonicalize a string which may not have been allocated using agstrdup.
90 */
91static char* canon (graph_t *g, char* s)
92{
93 char* ns = agstrdup (g, s);
94 char* cs = agcanonStr (ns);
95 agstrfree (g, ns);
96 return cs;
97}
98
99static void writenodeandport(FILE * f, node_t * node, char *port)
100{
101 char *name;
102 if (IS_CLUST_NODE(node))
103 name = canon (agraphof(node), strchr(agnameof(node), ':') + 1);
104 else
105 name = agcanonStr (agnameof(node));
106 printstring(f, " ", name); /* slimey i know */
107 if (port && *port)
108 printstring(f, ":", agcanonStr(port));
109}
110
111/* _write_plain:
112 */
113void write_plain(GVJ_t * job, graph_t * g, FILE * f, boolean extend)
114{
115 int i, j, splinePoints;
116 char *tport, *hport;
117 node_t *n;
118 edge_t *e;
119 bezier bz;
120 pointf pt;
121 char *lbl;
122 char* fillcolor;
123
124 putstr = g->clos->disc.io->putstr;
125// setup_graph(job, g);
126 setYInvert(g);
127 pt = GD_bb(g).UR;
128 printdouble(f, "graph ", job->zoom);
129 printdouble(f, " ", PS2INCH(pt.x));
130 printdouble(f, " ", PS2INCH(pt.y));
131 agputc('\n', f);
132 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
133 if (IS_CLUST_NODE(n))
134 continue;
135 printstring(f, "node ", agcanonStr(agnameof(n)));
136 printpoint(f, ND_coord(n));
137 if (ND_label(n)->html) /* if html, get original text */
138 lbl = agcanonStr (agxget(n, N_label));
139 else
140 lbl = canon(agraphof(n),ND_label(n)->text);
141 printdouble(f, " ", ND_width(n));
142 printdouble(f, " ", ND_height(n));
143 printstring(f, " ", lbl);
144 printstring(f, " ", late_nnstring(n, N_style, "solid"));
145 printstring(f, " ", ND_shape(n)->name);
146 printstring(f, " ", late_nnstring(n, N_color, DEFAULT_COLOR));
147 fillcolor = late_nnstring(n, N_fillcolor, "");
148 if (fillcolor[0] == '\0')
149 fillcolor = late_nnstring(n, N_color, DEFAULT_FILL);
150 printstring(f, " ", fillcolor);
151 agputc('\n', f);
152 }
153 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
154 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
155
156 if (extend) { //assuming these two attrs have already been created by cgraph
157 if (!(tport = agget(e,"tailport")))
158 tport = "";
159 if (!(hport = agget(e,"headport")))
160 hport = "";
161 }
162 else
163 tport = hport = "";
164 if (ED_spl(e)) {
165 splinePoints = 0;
166 for (i = 0; i < ED_spl(e)->size; i++) {
167 bz = ED_spl(e)->list[i];
168 splinePoints += bz.size;
169 }
170 printstring(f, NULL, "edge");
171 writenodeandport(f, agtail(e), tport);
172 writenodeandport(f, aghead(e), hport);
173 printint(f, " ", splinePoints);
174 for (i = 0; i < ED_spl(e)->size; i++) {
175 bz = ED_spl(e)->list[i];
176 for (j = 0; j < bz.size; j++)
177 printpoint(f, bz.list[j]);
178 }
179 }
180 if (ED_label(e)) {
181 printstring(f, " ", canon(agraphof(agtail(e)),ED_label(e)->text));
182 printpoint(f, ED_label(e)->pos);
183 }
184 printstring(f, " ", late_nnstring(e, E_style, "solid"));
185 printstring(f, " ", late_nnstring(e, E_color, DEFAULT_COLOR));
186 agputc('\n', f);
187 }
188 }
189 agputs("stop\n", f);
190}
191
192static void set_record_rects(node_t * n, field_t * f, agxbuf * xb)
193{
194 int i;
195 char buf[BUFSIZ];
196
197 if (f->n_flds == 0) {
198 sprintf(buf, "%.5g,%.5g,%.5g,%.5g ",
199 f->b.LL.x + ND_coord(n).x,
200 YDIR(f->b.LL.y + ND_coord(n).y),
201 f->b.UR.x + ND_coord(n).x,
202 YDIR(f->b.UR.y + ND_coord(n).y));
203 agxbput(xb, buf);
204 }
205 for (i = 0; i < f->n_flds; i++)
206 set_record_rects(n, f->fld[i], xb);
207}
208
209static void rec_attach_bb(graph_t * g, Agsym_t* bbsym, Agsym_t* lpsym, Agsym_t* lwsym, Agsym_t* lhsym)
210{
211 int c;
212 char buf[BUFSIZ];
213 pointf pt;
214
215 sprintf(buf, "%.5g,%.5g,%.5g,%.5g", GD_bb(g).LL.x, YDIR(GD_bb(g).LL.y),
216 GD_bb(g).UR.x, YDIR(GD_bb(g).UR.y));
217 agxset(g, bbsym, buf);
218 if (GD_label(g) && GD_label(g)->text[0]) {
219 pt = GD_label(g)->pos;
220 sprintf(buf, "%.5g,%.5g", pt.x, YDIR(pt.y));
221 agxset(g, lpsym, buf);
222 pt = GD_label(g)->dimen;
223 sprintf(buf, "%.2f", PS2INCH(pt.x));
224 agxset (g, lwsym, buf);
225 sprintf(buf, "%.2f", PS2INCH(pt.y));
226 agxset (g, lhsym, buf);
227 }
228 for (c = 1; c <= GD_n_cluster(g); c++)
229 rec_attach_bb(GD_clust(g)[c], bbsym, lpsym, lwsym, lhsym);
230}
231
232void attach_attrs_and_arrows(graph_t* g, int* sp, int* ep)
233{
234 int e_arrows; /* graph has edges with end arrows */
235 int s_arrows; /* graph has edges with start arrows */
236 int i, j, sides;
237 char buf[BUFSIZ]; /* Used only for small strings */
238 unsigned char xbuffer[BUFSIZ]; /* Initial buffer for xb */
239 agxbuf xb;
240 node_t *n;
241 edge_t *e;
242 pointf ptf;
243 int dim3 = (GD_odim(g) >= 3);
244 Agsym_t* bbsym = NULL;
245 Agsym_t* lpsym = NULL;
246 Agsym_t* lwsym = NULL;
247 Agsym_t* lhsym = NULL;
248
249 gv_fixLocale (1);
250 e_arrows = s_arrows = 0;
251 setYInvert(g);
252 agxbinit(&xb, BUFSIZ, xbuffer);
253 safe_dcl(g, AGNODE, "pos", "");
254 safe_dcl(g, AGNODE, "rects", "");
255 N_width = safe_dcl(g, AGNODE, "width", "");
256 N_height = safe_dcl(g, AGNODE, "height", "");
257 safe_dcl(g, AGEDGE, "pos", "");
258 if (GD_has_labels(g) & NODE_XLABEL)
259 safe_dcl(g, AGNODE, "xlp", "");
260 if (GD_has_labels(g) & EDGE_LABEL)
261 safe_dcl(g, AGEDGE, "lp", "");
262 if (GD_has_labels(g) & EDGE_XLABEL)
263 safe_dcl(g, AGEDGE, "xlp", "");
264 if (GD_has_labels(g) & HEAD_LABEL)
265 safe_dcl(g, AGEDGE, "head_lp", "");
266 if (GD_has_labels(g) & TAIL_LABEL)
267 safe_dcl(g, AGEDGE, "tail_lp", "");
268 if (GD_has_labels(g) & GRAPH_LABEL) {
269 lpsym = safe_dcl(g, AGRAPH, "lp", "");
270 lwsym = safe_dcl(g, AGRAPH, "lwidth", "");
271 lhsym = safe_dcl(g, AGRAPH, "lheight", "");
272 }
273 bbsym = safe_dcl(g, AGRAPH, "bb", "");
274 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
275 if (dim3) {
276 int k;
277
278 sprintf(buf, "%.5g,%.5g,%.5g", ND_coord(n).x, YDIR(ND_coord(n).y), POINTS_PER_INCH*(ND_pos(n)[2]));
279 agxbput (&xb, buf);
280 for (k = 3; k < GD_odim(g); k++) {
281 sprintf(buf, ",%.5g", POINTS_PER_INCH*(ND_pos(n)[k]));
282 agxbput (&xb, buf);
283 }
284 agset(n, "pos", agxbuse(&xb));
285 } else {
286 sprintf(buf, "%.5g,%.5g", ND_coord(n).x, YDIR(ND_coord(n).y));
287 agset(n, "pos", buf);
288 }
289 sprintf(buf, "%.5g", PS2INCH(ND_ht(n)));
290 agxset(n, N_height, buf);
291 sprintf(buf, "%.5g", PS2INCH(ND_lw(n) + ND_rw(n)));
292 agxset(n, N_width, buf);
293 if (ND_xlabel(n) && ND_xlabel(n)->set) {
294 ptf = ND_xlabel(n)->pos;
295 sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
296 agset(n, "xlp", buf);
297 }
298 if (strcmp(ND_shape(n)->name, "record") == 0) {
299 set_record_rects(n, ND_shape_info(n), &xb);
300 agxbpop(&xb); /* get rid of last space */
301 agset(n, "rects", agxbuse(&xb));
302 } else {
303 polygon_t *poly;
304 int i;
305 if (N_vertices && isPolygon(n)) {
306 poly = (polygon_t *) ND_shape_info(n);
307 sides = poly->sides;
308 if (sides < 3) {
309 char *p = agget(n, "samplepoints");
310 if (p)
311 sides = atoi(p);
312 else
313 sides = 8;
314 if (sides < 3)
315 sides = 8;
316 }
317 for (i = 0; i < sides; i++) {
318 if (i > 0)
319 agxbputc(&xb, ' ');
320 if (poly->sides >= 3)
321 sprintf(buf, "%.5g %.5g",
322 PS2INCH(poly->vertices[i].x),
323 YFDIR(PS2INCH(poly->vertices[i].y)));
324 else
325 sprintf(buf, "%.5g %.5g",
326 ND_width(n) / 2.0 * cos(i / (double) sides * M_PI * 2.0),
327 YFDIR(ND_height(n) / 2.0 * sin(i / (double) sides * M_PI * 2.0)));
328 agxbput(&xb, buf);
329 }
330 agxset(n, N_vertices, agxbuse(&xb));
331 }
332 }
333 if (State >= GVSPLINES) {
334 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
335 if (ED_edge_type(e) == IGNORED)
336 continue;
337 if (ED_spl(e) == NULL)
338 continue; /* reported in postproc */
339 for (i = 0; i < ED_spl(e)->size; i++) {
340 if (i > 0)
341 agxbputc(&xb, ';');
342 if (ED_spl(e)->list[i].sflag) {
343 s_arrows = 1;
344 sprintf(buf, "s,%.5g,%.5g ",
345 ED_spl(e)->list[i].sp.x,
346 YDIR(ED_spl(e)->list[i].sp.y));
347 agxbput(&xb, buf);
348 }
349 if (ED_spl(e)->list[i].eflag) {
350 e_arrows = 1;
351 sprintf(buf, "e,%.5g,%.5g ",
352 ED_spl(e)->list[i].ep.x,
353 YDIR(ED_spl(e)->list[i].ep.y));
354 agxbput(&xb, buf);
355 }
356 for (j = 0; j < ED_spl(e)->list[i].size; j++) {
357 if (j > 0)
358 agxbputc(&xb, ' ');
359 ptf = ED_spl(e)->list[i].list[j];
360 sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
361 agxbput(&xb, buf);
362 }
363 }
364 agset(e, "pos", agxbuse(&xb));
365 if (ED_label(e)) {
366 ptf = ED_label(e)->pos;
367 sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
368 agset(e, "lp", buf);
369 }
370 if (ED_xlabel(e) && ED_xlabel(e)->set) {
371 ptf = ED_xlabel(e)->pos;
372 sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
373 agset(e, "xlp", buf);
374 }
375 if (ED_head_label(e)) {
376 ptf = ED_head_label(e)->pos;
377 sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
378 agset(e, "head_lp", buf);
379 }
380 if (ED_tail_label(e)) {
381 ptf = ED_tail_label(e)->pos;
382 sprintf(buf, "%.5g,%.5g", ptf.x, YDIR(ptf.y));
383 agset(e, "tail_lp", buf);
384 }
385 }
386 }
387 }
388 rec_attach_bb(g, bbsym, lpsym, lwsym, lhsym);
389 agxbfree(&xb);
390
391 if (HAS_CLUST_EDGE(g))
392 undoClusterEdges(g);
393
394 *sp = s_arrows;
395 *ep = e_arrows;
396 gv_fixLocale (0);
397}
398
399void attach_attrs(graph_t * g)
400{
401 int e, s;
402 attach_attrs_and_arrows (g, &s, &e);
403}
404
405