Graphviz  2.41.20171026.1811
gvrender_gd_vrml.c
Go to the documentation of this file.
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 
15 #include "config.h"
16 
17 #include <stdlib.h>
18 #include <stddef.h>
19 #include <string.h>
20 #include <fcntl.h>
21 
22 #include "gvplugin_render.h"
23 #include "gvio.h"
24 #include "gd.h"
25 
26 #ifdef HAVE_GD_PNG
27 
28 /* for N_GNEW() */
29 #include "memory.h"
30 
31 /* for gvcolor_t */
32 #include "color.h"
33 
34 /* for late_double() */
35 #include "agxbuf.h"
36 #include "utils.h"
37 
38 /* for wind() */
39 #include "pathutil.h"
40 
41 extern shape_kind shapeOf(node_t *);
42 extern pointf gvrender_ptf(GVJ_t *job, pointf p);
43 extern pointf Bezier(pointf * V, int degree, double t, pointf * Left, pointf * Right);
44 
45 typedef enum { FORMAT_VRML, } format_type;
46 
47 #define BEZIERSUBDIVISION 10
48 
49 /* static int N_pages; */
50 /* static point Pages; */
51 static double Scale;
52 static double MinZ;
53 /* static int onetime = TRUE; */
54 static int Saw_skycolor;
55 
56 static gdImagePtr im;
57 static FILE *PNGfile;
58 static int IsSegment; /* set true if edge is line segment */
59 static double CylHt; /* height of cylinder part of edge */
60 static double EdgeLen; /* length between centers of endpoints */
61 static double HeadHt, TailHt; /* height of arrows */
62 static double Fstz, Sndz; /* z values of tail and head points */
63 
64 /* gdirname:
65  * Returns directory pathname prefix
66  * Code adapted from dgk
67  */
68 static char *gdirname(char *pathname)
69 {
70  char *last;
71 
72  /* go to end of path */
73  for (last = pathname; *last; last++);
74  /* back over trailing '/' */
75  while (last > pathname && *--last == '/');
76  /* back over non-slash chars */
77  for (; last > pathname && *last != '/'; last--);
78  if (last == pathname) {
79  /* all '/' or "" */
80  if (*pathname != '/')
81  *last = '.';
82  /* preserve // */
83  else if (pathname[1] == '/')
84  last++;
85  } else {
86  /* back over trailing '/' */
87  for (; *last == '/' && last > pathname; last--);
88  /* preserve // */
89  if (last == pathname && *pathname == '/' && pathname[1] == '/')
90  last++;
91  }
92  last++;
93  *last = '\0';
94 
95  return pathname;
96 }
97 
98 static char *nodefilename(const char *filename, node_t * n, char *buf)
99 {
100  static char *dir;
101  static char disposable[1024];
102 
103  if (dir == 0) {
104  if (filename)
105  dir = gdirname(strcpy(disposable, filename));
106  else
107  dir = ".";
108  }
109  sprintf(buf, "%s/node%d.png", dir, AGSEQ(n));
110  return buf;
111 }
112 
113 static FILE *nodefile(const char *filename, node_t * n)
114 {
115  FILE *rv;
116  char buf[1024];
117 
118  rv = fopen(nodefilename(filename, n, buf), "wb");
119  return rv;
120 }
121 
122 #define NODE_PAD 1
123 
124 static pointf vrml_node_point(GVJ_t *job, node_t *n, pointf p)
125 {
126  pointf rv;
127 
128  /* make rv relative to PNG canvas */
129  if (job->rotation) {
130  rv.x = ( (p.y - job->pad.y) - ND_coord(n).y + ND_lw(n) ) * Scale + NODE_PAD;
131  rv.y = (-(p.x - job->pad.x) + ND_coord(n).x + ND_ht(n) / 2.) * Scale + NODE_PAD;
132  } else {
133  rv.x = ( (p.x - job->pad.x) - ND_coord(n).x + ND_lw(n) ) * Scale + NODE_PAD;
134  rv.y = (-(p.y - job->pad.y) + ND_coord(n).y + ND_ht(n) / 2.) * Scale + NODE_PAD;
135  }
136  return rv;
137 }
138 
139 static int color_index(gdImagePtr im, gvcolor_t color)
140 {
141  int alpha;
142 
143  /* convert alpha (normally an "opacity" value) to gd's "transparency" */
144  alpha = (255 - color.u.rgba[3]) * gdAlphaMax / 255;
145 
146  if(alpha == gdAlphaMax)
147  return (gdImageGetTransparent(im));
148  else
149  return (gdImageColorResolveAlpha(im,
150  color.u.rgba[0],
151  color.u.rgba[1],
152  color.u.rgba[2],
153  alpha));
154 }
155 
156 static int set_penstyle(GVJ_t * job, gdImagePtr im, gdImagePtr brush)
157 {
158  obj_state_t *obj = job->obj;
159  int i, pen, pencolor, transparent, width, dashstyle[40];
160 
161  pen = pencolor = color_index(im, obj->pencolor);
162  transparent = gdImageGetTransparent(im);
163  if (obj->pen == PEN_DASHED) {
164  for (i = 0; i < 20; i++)
165  dashstyle[i] = pencolor;
166  for (; i < 40; i++)
167  dashstyle[i] = transparent;
168  gdImageSetStyle(im, dashstyle, 20);
169  pen = gdStyled;
170  } else if (obj->pen == PEN_DOTTED) {
171  for (i = 0; i < 2; i++)
172  dashstyle[i] = pencolor;
173  for (; i < 24; i++)
174  dashstyle[i] = transparent;
175  gdImageSetStyle(im, dashstyle, 24);
176  pen = gdStyled;
177  }
178  width = obj->penwidth * job->scale.x;
179  if (width < PENWIDTH_NORMAL)
180  width = PENWIDTH_NORMAL; /* gd can't do thin lines */
181  gdImageSetThickness(im, width);
182  /* use brush instead of Thickness to improve end butts */
183  if (width != PENWIDTH_NORMAL) {
184  brush = gdImageCreate(width, width);
185  gdImagePaletteCopy(brush, im);
186  gdImageFilledRectangle(brush, 0, 0, width - 1, width - 1, pencolor);
187  gdImageSetBrush(im, brush);
188  if (pen == gdStyled)
189  pen = gdStyledBrushed;
190  else
191  pen = gdBrushed;
192  }
193  return pen;
194 }
195 
196 /* warmed over VRML code starts here */
197 
198 static void vrml_begin_page(GVJ_t *job)
199 {
200  Scale = (double) DEFAULT_DPI / POINTS_PER_INCH;
201  gvputs(job, "#VRML V2.0 utf8\n");
202 
203  Saw_skycolor = FALSE;
204  MinZ = MAXDOUBLE;
205  gvputs(job, "Group { children [\n");
206  gvputs(job, " Transform {\n");
207  gvprintf(job, " scale %.3f %.3f %.3f\n", .0278, .0278, .0278);
208  gvputs(job, " children [\n");
209 }
210 
211 static void vrml_end_page(GVJ_t *job)
212 {
213  double d, z;
214  box bb = job->boundingBox;
215 
216  d = MAX(bb.UR.x - bb.LL.x,bb.UR.y - bb.LL.y);
217  /* Roughly fill 3/4 view assuming FOV angle of M_PI/4.
218  * Small graphs and non-square aspect ratios will upset this.
219  */
220  z = (0.6667*d)/tan(M_PI/8.0) + MinZ; /* fill 3/4 of view */
221 
222  if (!Saw_skycolor)
223  gvputs(job, " Background { skyColor 1 1 1 }\n");
224  gvputs(job, " ] }\n");
225  gvprintf(job, " Viewpoint {position %.3f %.3f %.3f}\n",
226  Scale * (bb.UR.x + bb.LL.x) / 72.,
227  Scale * (bb.UR.y + bb.LL.y) / 72.,
228  Scale * 2 * z / 72.);
229  gvputs(job, "] }\n");
230 }
231 
232 static void vrml_begin_node(GVJ_t *job)
233 {
234  obj_state_t *obj = job->obj;
235  node_t *n = obj->u.n;
236  double z = obj->z;
237  int width, height;
238  int transparent;
239 
240  gvprintf(job, "# node %s\n", agnameof(n));
241  if (z < MinZ)
242  MinZ = z;
243  if (shapeOf(n) != SH_POINT) {
244  PNGfile = nodefile(job->output_filename, n);
245 
246  width = (ND_lw(n) + ND_rw(n)) * Scale + 2 * NODE_PAD;
247  height = (ND_ht(n) ) * Scale + 2 * NODE_PAD;
248  im = gdImageCreate(width, height);
249 
250  /* make background transparent */
251  transparent = gdImageColorResolveAlpha(im,
252  gdRedMax - 1, gdGreenMax,
253  gdBlueMax, gdAlphaTransparent);
254  gdImageColorTransparent(im, transparent);
255  }
256 }
257 
258 static void vrml_end_node(GVJ_t *job)
259 {
260  if (im) {
261  gdImagePng(im, PNGfile);
262  fclose(PNGfile);
263  gdImageDestroy(im);
264  im = NULL;
265  }
266 }
267 
268 static void vrml_begin_edge(GVJ_t *job)
269 {
270  obj_state_t *obj = job->obj;
271  edge_t *e = obj->u.e;
272 
273  IsSegment = 0;
274  gvprintf(job, "# edge %s -> %s\n", agnameof(agtail(e)), agnameof(aghead(e)));
275  gvputs(job, " Group { children [\n");
276 }
277 
278 static void
279 finishSegment (GVJ_t *job, edge_t *e)
280 {
281  pointf p0 = gvrender_ptf(job, ND_coord(agtail(e)));
282  pointf p1 = gvrender_ptf(job, ND_coord(aghead(e)));
283  double o_x, o_y, o_z;
284  double x, y, y0, z, theta;
285 
286  o_x = ((double)(p0.x + p1.x))/2;
287  o_y = ((double)(p0.y + p1.y))/2;
288  o_z = (Fstz + Sndz)/2;
289  /* Compute rotation */
290  /* Pick end point with highest y */
291  if (p0.y > p1.y) {
292  x = p0.x;
293  y = p0.y;
294  z = Fstz;
295  }
296  else {
297  x = p1.x;
298  y = p1.y;
299  z = Sndz;
300  }
301  /* Translate center to the origin */
302  x -= o_x;
303  y -= o_y;
304  z -= o_z;
305  if (p0.y > p1.y)
306  theta = acos(2*y/EdgeLen) + M_PI;
307  else
308  theta = acos(2*y/EdgeLen);
309  if (!x && !z) /* parallel to y-axis */
310  x = 1;
311 
312  y0 = (HeadHt-TailHt)/2.0;
313  gvputs(job, " ]\n");
314  gvprintf(job, " center 0 %.3f 0\n", y0);
315  gvprintf(job, " rotation %.3f 0 %.3f %.3f\n", -z, x, -theta);
316  gvprintf(job, " translation %.3f %.3f %.3f\n", o_x, o_y - y0, o_z);
317  gvputs(job, " }\n");
318 }
319 
320 static void vrml_end_edge(GVJ_t *job)
321 {
322  if (IsSegment)
323  finishSegment(job, job->obj->u.e);
324  gvputs(job, "] }\n");
325 }
326 
327 extern void gdgen_text(gdImagePtr im, pointf spf, pointf epf, int fontcolor, double fontsize, int fontdpi, double fontangle, char *fontname, char *str);
328 
329 static void vrml_textspan(GVJ_t *job, pointf p, textspan_t * span)
330 {
331  obj_state_t *obj = job->obj;
332  pointf spf, epf, q;
333 
334  if (! obj->u.n || ! im) /* if not a node - or if no im (e.g. for cluster) */
335  return;
336 
337  switch (span->just) {
338  case 'l':
339  break;
340  case 'r':
341  p.x -= span->size.x;
342  break;
343  default:
344  case 'n':
345  p.x -= span->size.x / 2;
346  break;
347  }
348  q.x = p.x + span->size.x;
349  q.y = p.y;
350 
351  spf = vrml_node_point(job, obj->u.n, p);
352  epf = vrml_node_point(job, obj->u.n, q);
353 
354  gdgen_text(im, spf, epf,
355  color_index(im, obj->pencolor),
356  span->font->size,
357  DEFAULT_DPI,
358  job->rotation ? (M_PI / 2) : 0,
359  span->font->name,
360  span->str);
361 }
362 
363 /* interpolate_zcoord:
364  * Given 2 points in 3D p = (fst.x,fst.y,fstz) and q = (snd.x, snd.y, sndz),
365  * and a point p1 in the xy plane lying on the line segment connecting
366  * the projections of the p and q, find the z coordinate of p1 when it
367  * is projected up onto the segment (p,q) in 3-space.
368  *
369  * Why the special case for ranks? Is the arithmetic really correct?
370  */
371 static double
372 interpolate_zcoord(GVJ_t *job, pointf p1, pointf fst, double fstz, pointf snd, double sndz)
373 {
374  obj_state_t *obj = job->obj;
375  edge_t *e = obj->u.e;
376  double len, d, rv;
377 
378  if (fstz == sndz)
379  return fstz;
380  if (ND_rank(agtail(e)) != ND_rank(aghead(e))) {
381  if (snd.y == fst.y)
382  rv = (fstz + sndz) / 2.0;
383  else
384  rv = fstz + (sndz - fstz) * (p1.y - fst.y) / (snd.y - fst.y);
385  }
386  else {
387  len = DIST(fst, snd);
388  d = DIST(p1, fst)/len;
389  rv = fstz + d*(sndz - fstz);
390  }
391  return rv;
392 }
393 
394 /* collinear:
395  * Return true if the 3 points starting at A are collinear.
396  */
397 static int
398 collinear (pointf * A)
399 {
400  double w;
401 
402  w = wind(A[0],A[1],A[2]);
403  return (fabs(w) <= 1);
404 }
405 
406 /* straight:
407  * Return true if bezier points are collinear
408  * At present, just check with 4 points, the common case.
409  */
410 static int
411 straight (pointf * A, int n)
412 {
413  if (n != 4) return 0;
414  return (collinear(A) && collinear(A+1));
415 }
416 
417 static void
418 doSegment (GVJ_t *job, pointf* A, pointf p0, double z0, pointf p1, double z1)
419 {
420  obj_state_t *obj = job->obj;
421  double d1, d0;
422  double delx, dely, delz;
423 
424  delx = p0.x - p1.x;
425  dely = p0.y - p1.y;
426  delz = z0 - z1;
427  EdgeLen = sqrt(delx*delx + dely*dely + delz*delz);
428  d0 = DIST(A[0],p0);
429  d1 = DIST(A[3],p1);
430  CylHt = EdgeLen - d0 - d1;
431  TailHt = HeadHt = 0;
432 
433  IsSegment = 1;
434  gvputs(job, "Transform {\n");
435  gvputs(job, " children [\n");
436  gvputs(job, " Shape {\n");
437  gvputs(job, " geometry Cylinder {\n");
438  gvputs(job, " bottom FALSE top FALSE\n");
439  gvprintf(job, " height %.3f radius %.3f }\n", CylHt, obj->penwidth);
440  gvputs(job, " appearance Appearance {\n");
441  gvputs(job, " material Material {\n");
442  gvputs(job, " ambientIntensity 0.33\n");
443  gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
444  obj->pencolor.u.rgba[0] / 255.,
445  obj->pencolor.u.rgba[1] / 255.,
446  obj->pencolor.u.rgba[2] / 255.);
447  gvputs(job, " }\n");
448  gvputs(job, " }\n");
449  gvputs(job, " }\n");
450 }
451 
452 /* nearTail:
453  * Given a point a and edge e, return true if a is closer to the
454  * tail of e than the head.
455  */
456 static int
457 nearTail (GVJ_t* job, pointf a, Agedge_t* e)
458 {
459  pointf tp = gvrender_ptf(job, ND_coord(agtail(e)));
460  pointf hp = gvrender_ptf(job, ND_coord(aghead(e)));
461 
462  return (DIST2(a, tp) < DIST2(a, hp));
463 }
464 
465  /* this is gruesome, but how else can we get z coord */
466 #define GETZ(jp,op,p,e) (nearTail(jp,p,e)?op->tail_z:op->head_z)
467 
468 static void
469 vrml_bezier(GVJ_t *job, pointf * A, int n, int arrow_at_start, int arrow_at_end, int filled)
470 {
471  obj_state_t *obj = job->obj;
472  edge_t *e = obj->u.e;
473  double fstz, sndz;
474  pointf p1, V[4];
475  int i, j, step;
476 
477  assert(e);
478 
479  fstz = Fstz = obj->tail_z;
480  sndz = Sndz = obj->head_z;
481  if (straight(A,n)) {
482  doSegment (job, A, gvrender_ptf(job, ND_coord(agtail(e))),Fstz,gvrender_ptf(job, ND_coord(aghead(e))),Sndz);
483  return;
484  }
485 
486  gvputs(job, "Shape { geometry Extrusion {\n");
487  gvputs(job, " spine [");
488  V[3] = A[0];
489  for (i = 0; i + 3 < n; i += 3) {
490  V[0] = V[3];
491  for (j = 1; j <= 3; j++)
492  V[j] = A[i + j];
493  for (step = 0; step <= BEZIERSUBDIVISION; step++) {
494  p1 = Bezier(V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL);
495  gvprintf(job, " %.3f %.3f %.3f", p1.x, p1.y,
496  interpolate_zcoord(job, p1, A[0], fstz, A[n - 1], sndz));
497  }
498  }
499  gvputs(job, " ]\n");
500  gvprintf(job, " crossSection [ %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f ]\n",
501  (obj->penwidth), (obj->penwidth), -(obj->penwidth),
502  (obj->penwidth), -(obj->penwidth), -(obj->penwidth),
503  (obj->penwidth), -(obj->penwidth));
504  gvputs(job, "}\n");
505  gvprintf(job, " appearance DEF E%ld Appearance {\n", AGSEQ(e));
506  gvputs(job, " material Material {\n");
507  gvputs(job, " ambientIntensity 0.33\n");
508  gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
509  obj->pencolor.u.rgba[0] / 255.,
510  obj->pencolor.u.rgba[1] / 255.,
511  obj->pencolor.u.rgba[2] / 255.);
512  gvputs(job, " }\n");
513  gvputs(job, " }\n");
514  gvputs(job, "}\n");
515 }
516 
517 /* doArrowhead:
518  * If edge is straight, we attach a cone to the edge as a group.
519  */
520 static void doArrowhead (GVJ_t *job, pointf * A)
521 {
522  obj_state_t *obj = job->obj;
523  edge_t *e = obj->u.e;
524  double rad, ht, y;
525  pointf p0; /* center of triangle base */
526 
527  p0.x = (A[0].x + A[2].x)/2.0;
528  p0.y = (A[0].y + A[2].y)/2.0;
529  rad = DIST(A[0],A[2])/2.0;
530  ht = DIST(p0,A[1]);
531 
532  y = (CylHt + ht)/2.0;
533 
534  gvputs(job, "Transform {\n");
535  if (nearTail (job, A[1], e)) {
536  TailHt = ht;
537  gvprintf(job, " translation 0 %.3f 0\n", -y);
538  gvprintf(job, " rotation 0 0 1 %.3f\n", M_PI);
539  }
540  else {
541  HeadHt = ht;
542  gvprintf(job, " translation 0 %.3f 0\n", y);
543  }
544  gvputs(job, " children [\n");
545  gvputs(job, " Shape {\n");
546  gvprintf(job, " geometry Cone {bottomRadius %.3f height %.3f }\n",
547  rad, ht);
548  gvputs(job, " appearance Appearance {\n");
549  gvputs(job, " material Material {\n");
550  gvputs(job, " ambientIntensity 0.33\n");
551  gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
552  obj->pencolor.u.rgba[0] / 255.,
553  obj->pencolor.u.rgba[1] / 255.,
554  obj->pencolor.u.rgba[2] / 255.);
555  gvputs(job, " }\n");
556  gvputs(job, " }\n");
557  gvputs(job, " }\n");
558  gvputs(job, " ]\n");
559  gvputs(job, "}\n");
560 }
561 
562 static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled)
563 {
564  obj_state_t *obj = job->obj;
565  node_t *n;
566  edge_t *e;
567  double z = obj->z;
568  pointf p, mp;
569  gdPoint *points;
570  int i, pen;
571  gdImagePtr brush = NULL;
572  double theta;
573 
574  switch (obj->type) {
575  case ROOTGRAPH_OBJTYPE:
576  gvprintf(job, " Background { skyColor %.3f %.3f %.3f }\n",
577  obj->fillcolor.u.rgba[0] / 255.,
578  obj->fillcolor.u.rgba[1] / 255.,
579  obj->fillcolor.u.rgba[2] / 255.);
580  Saw_skycolor = TRUE;
581  break;
582  case CLUSTER_OBJTYPE:
583  break;
584  case NODE_OBJTYPE:
585  n = obj->u.n;
586  pen = set_penstyle(job, im, brush);
587  points = N_GGNEW(np, gdPoint);
588  for (i = 0; i < np; i++) {
589  mp = vrml_node_point(job, n, A[i]);
590  points[i].x = ROUND(mp.x);
591  points[i].y = ROUND(mp.y);
592  }
593  if (filled)
594  gdImageFilledPolygon(im, points, np, color_index(im, obj->fillcolor));
595  gdImagePolygon(im, points, np, pen);
596  free(points);
597  if (brush)
598  gdImageDestroy(brush);
599 
600  gvputs(job, "Shape {\n");
601  gvputs(job, " appearance Appearance {\n");
602  gvputs(job, " material Material {\n");
603  gvputs(job, " ambientIntensity 0.33\n");
604  gvputs(job, " diffuseColor 1 1 1\n");
605  gvputs(job, " }\n");
606  gvprintf(job, " texture ImageTexture { url \"node%ld.png\" }\n", AGSEQ(n));
607  gvputs(job, " }\n");
608  gvputs(job, " geometry Extrusion {\n");
609  gvputs(job, " crossSection [");
610  for (i = 0; i < np; i++) {
611  p.x = A[i].x - ND_coord(n).x;
612  p.y = A[i].y - ND_coord(n).y;
613  gvprintf(job, " %.3f %.3f,", p.x, p.y);
614  }
615  p.x = A[0].x - ND_coord(n).x;
616  p.y = A[0].y - ND_coord(n).y;
617  gvprintf(job, " %.3f %.3f ]\n", p.x, p.y);
618  gvprintf(job, " spine [ %.5g %.5g %.5g, %.5g %.5g %.5g ]\n",
619  ND_coord(n).x, ND_coord(n).y, z - .01,
620  ND_coord(n).x, ND_coord(n).y, z + .01);
621  gvputs(job, " }\n");
622  gvputs(job, "}\n");
623  break;
624  case EDGE_OBJTYPE:
625  e = obj->u.e;
626  if (np != 3) {
627  static int flag;
628  if (!flag) {
629  flag++;
630  agerr(AGWARN,
631  "vrml_polygon: non-triangle arrowheads not supported - ignoring\n");
632  }
633  }
634  if (IsSegment) {
635  doArrowhead (job, A);
636  return;
637  }
638  p.x = p.y = 0.0;
639  for (i = 0; i < np; i++) {
640  p.x += A[i].x;
641  p.y += A[i].y;
642  }
643  p.x = p.x / np;
644  p.y = p.y / np;
645 
646  /* it is bad to know that A[1] is the aiming point, but we do */
647  theta =
648  atan2((A[0].y + A[2].y) / 2.0 - A[1].y,
649  (A[0].x + A[2].x) / 2.0 - A[1].x) + M_PI / 2.0;
650 
651  z = GETZ(job,obj,p,e);
652 
653  /* FIXME: arrow vector ought to follow z coord of bezier */
654  gvputs(job, "Transform {\n");
655  gvprintf(job, " translation %.3f %.3f %.3f\n", p.x, p.y, z);
656  gvputs(job, " children [\n");
657  gvputs(job, " Transform {\n");
658  gvprintf(job, " rotation 0 0 1 %.3f\n", theta);
659  gvputs(job, " children [\n");
660  gvputs(job, " Shape {\n");
661  gvprintf(job, " geometry Cone {bottomRadius %.3f height %.3f }\n",
662  obj->penwidth * 2.5, obj->penwidth * 10.0);
663  gvprintf(job, " appearance USE E%ld\n", AGSEQ(e));
664  gvputs(job, " }\n");
665  gvputs(job, " ]\n");
666  gvputs(job, " }\n");
667  gvputs(job, " ]\n");
668  gvputs(job, "}\n");
669  break;
670  }
671 }
672 
673 /* doSphere:
674  * Output sphere in VRML for point nodes.
675  */
676 static void
677 doSphere (GVJ_t *job, node_t *n, pointf p, double z, double rx, double ry)
678 {
679  obj_state_t *obj = job->obj;
680 
681 // if (!(strcmp(cstk[SP].fillcolor, "transparent"))) {
682 // return;
683 // }
684 
685  gvputs(job, "Transform {\n");
686  gvprintf(job, " translation %.3f %.3f %.3f\n", p.x, p.y, z);
687  gvprintf(job, " scale %.3f %.3f %.3f\n", rx, rx, rx);
688  gvputs(job, " children [\n");
689  gvputs(job, " Transform {\n");
690  gvputs(job, " children [\n");
691  gvputs(job, " Shape {\n");
692  gvputs(job, " geometry Sphere { radius 1.0 }\n");
693  gvputs(job, " appearance Appearance {\n");
694  gvputs(job, " material Material {\n");
695  gvputs(job, " ambientIntensity 0.33\n");
696  gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
697  obj->pencolor.u.rgba[0] / 255.,
698  obj->pencolor.u.rgba[1] / 255.,
699  obj->pencolor.u.rgba[2] / 255.);
700  gvputs(job, " }\n");
701  gvputs(job, " }\n");
702  gvputs(job, " }\n");
703  gvputs(job, " ]\n");
704  gvputs(job, " }\n");
705  gvputs(job, " ]\n");
706  gvputs(job, "}\n");
707 }
708 
709 static void vrml_ellipse(GVJ_t * job, pointf * A, int filled)
710 {
711  obj_state_t *obj = job->obj;
712  node_t *n;
713  edge_t *e;
714  double z = obj->z;
715  double rx, ry;
716  int dx, dy;
717  pointf npf, nqf;
718  point np;
719  int pen;
720  gdImagePtr brush = NULL;
721 
722  rx = A[1].x - A[0].x;
723  ry = A[1].y - A[0].y;
724 
725  switch (obj->type) {
726  case ROOTGRAPH_OBJTYPE:
727  case CLUSTER_OBJTYPE:
728  break;
729  case NODE_OBJTYPE:
730  n = obj->u.n;
731  if (shapeOf(n) == SH_POINT) {
732  doSphere (job, n, A[0], z, rx, ry);
733  return;
734  }
735  pen = set_penstyle(job, im, brush);
736 
737  npf = vrml_node_point(job, n, A[0]);
738  nqf = vrml_node_point(job, n, A[1]);
739 
740  dx = ROUND(2 * (nqf.x - npf.x));
741  dy = ROUND(2 * (nqf.y - npf.y));
742 
743  PF2P(npf, np);
744 
745  if (filled)
746  gdImageFilledEllipse(im, np.x, np.y, dx, dy, color_index(im, obj->fillcolor));
747  gdImageArc(im, np.x, np.y, dx, dy, 0, 360, pen);
748 
749  if (brush)
750  gdImageDestroy(brush);
751 
752  gvputs(job, "Transform {\n");
753  gvprintf(job, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z);
754  gvprintf(job, " scale %.3f %.3f 1\n", rx, ry);
755  gvputs(job, " children [\n");
756  gvputs(job, " Transform {\n");
757  gvputs(job, " rotation 1 0 0 1.57\n");
758  gvputs(job, " children [\n");
759  gvputs(job, " Shape {\n");
760  gvputs(job, " geometry Cylinder { side FALSE }\n");
761  gvputs(job, " appearance Appearance {\n");
762  gvputs(job, " material Material {\n");
763  gvputs(job, " ambientIntensity 0.33\n");
764  gvputs(job, " diffuseColor 1 1 1\n");
765  gvputs(job, " }\n");
766  gvprintf(job, " texture ImageTexture { url \"node%ld.png\" }\n", AGSEQ(n));
767  gvputs(job, " }\n");
768  gvputs(job, " }\n");
769  gvputs(job, " ]\n");
770  gvputs(job, " }\n");
771  gvputs(job, " ]\n");
772  gvputs(job, "}\n");
773  break;
774  case EDGE_OBJTYPE:
775  e = obj->u.e;
776  z = GETZ(job,obj,A[0],e);
777 
778  gvputs(job, "Transform {\n");
779  gvprintf(job, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z);
780  gvputs(job, " children [\n");
781  gvputs(job, " Shape {\n");
782  gvprintf(job, " geometry Sphere {radius %.3f }\n", (double) rx);
783  gvprintf(job, " appearance USE E%d\n", AGSEQ(e));
784  gvputs(job, " }\n");
785  gvputs(job, " ]\n");
786  gvputs(job, "}\n");
787  }
788 }
789 
790 static gvrender_engine_t vrml_engine = {
791  0, /* vrml_begin_job */
792  0, /* vrml_end_job */
793  0, /* vrml_begin_graph */
794  0, /* vrml_end_graph */
795  0, /* vrml_begin_layer */
796  0, /* vrml_end_layer */
797  vrml_begin_page,
798  vrml_end_page,
799  0, /* vrml_begin_cluster */
800  0, /* vrml_end_cluster */
801  0, /* vrml_begin_nodes */
802  0, /* vrml_end_nodes */
803  0, /* vrml_begin_edges */
804  0, /* vrml_end_edges */
805  vrml_begin_node,
806  vrml_end_node,
807  vrml_begin_edge,
808  vrml_end_edge,
809  0, /* vrml_begin_anchor */
810  0, /* vrml_end_anchor */
811  0, /* vrml_begin_label */
812  0, /* vrml_end_label */
813  vrml_textspan,
814  0, /* vrml_resolve_color */
815  vrml_ellipse,
816  vrml_polygon,
817  vrml_bezier,
818  0, /* vrml_polyline - FIXME */
819  0, /* vrml_comment */
820  0, /* vrml_library_shape */
821 };
822 
823 static gvrender_features_t render_features_vrml = {
824  GVRENDER_DOES_Z, /* flags */
825  0., /* default pad - graph units */
826  NULL, /* knowncolors */
827  0, /* sizeof knowncolors */
828  RGBA_BYTE, /* color_type */
829 };
830 
831 static gvdevice_features_t device_features_vrml = {
833  | GVDEVICE_NO_WRITER, /* flags */
834  {0.,0.}, /* default margin - points */
835  {0.,0.}, /* default page width, height - points */
836  {72.,72.}, /* default dpi */
837 };
838 #endif /* HAVE_GD_PNG */
839 
841 #ifdef HAVE_GD_PNG
842  {FORMAT_VRML, "vrml", 1, &vrml_engine, &render_features_vrml},
843 #endif
844  {0, NULL, 0, NULL, NULL}
845 };
846 
848 #ifdef HAVE_GD_PNG
849  {FORMAT_VRML, "vrml:vrml", 1, NULL, &device_features_vrml},
850 #endif
851  {0, NULL, 0, NULL, NULL}
852 };
#define MAX(a, b)
Definition: agerror.c:17
#define AGSEQ(obj)
Definition: cgraph.h:115
#define PENWIDTH_NORMAL
Definition: gvcjob.h:40
pointf size
Definition: textspan.h:64
#define ND_rank(n)
Definition: types.h:529
int rotation
Definition: gvcjob.h:328
box boundingBox
Definition: gvcjob.h:339
union color_s::@10 u
void gdgen_text(gdImagePtr im, pointf spf, pointf epf, int fontcolor, double fontsize, int fontdpi, double fontangle, char *fontname, char *str)
Definition: gvrender_gd.c:298
#define GVDEVICE_BINARY_FORMAT
Definition: gvcjob.h:93
shape_kind shapeOf(node_t *)
Definition: shapes.c:1820
pen_type pen
Definition: gvcjob.h:206
double size
Definition: textspan.h:52
#define ROUND(f)
Definition: arith.h:84
#define assert(x)
Definition: cghdr.h:47
Definition: geom.h:28
#define PF2P(pf, p)
Definition: geom.h:72
double head_z
Definition: gvcjob.h:211
Definition: color.h:34
const char * output_filename
Definition: gvcjob.h:285
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:141
gvcolor_t pencolor
Definition: gvcjob.h:203
gvplugin_installed_t gvdevice_vrml_types[]
Definition: gvcjob.h:271
char * name
Definition: textspan.h:49
point UR
Definition: geom.h:33
int x
Definition: geom.h:26
#define POINTS_PER_INCH
Definition: geom.h:62
shape_kind
Definition: types.h:186
Definition: cgraph.h:388
obj_state_t * obj
Definition: gvcjob.h:278
CGRAPH_API Agnode_t * agtail(Agedge_t *e)
Definition: edge.c:525
#define GVDEVICE_NO_WRITER
Definition: gvcjob.h:95
int gvputs(GVJ_t *job, const char *s)
Definition: gvdevice.c:270
#define DIST(p, q)
Definition: geom.h:60
char * str
Definition: textspan.h:59
#define GVRENDER_DOES_Z
Definition: gvcjob.h:108
#define ND_ht(n)
Definition: types.h:506
pointf pad
Definition: gvcjob.h:321
CGRAPH_API Agnode_t * aghead(Agedge_t *e)
Definition: edge.c:533
#define DIST2(p, q)
Definition: geom.h:59
double y
Definition: geom.h:28
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
obj_type type
Definition: gvcjob.h:193
pointf scale
Definition: gvcjob.h:341
#define ND_rw(n)
Definition: types.h:531
point LL
Definition: geom.h:33
pointf gvrender_ptf(GVJ_t *job, pointf p)
Definition: gvrender.c:140
int wind(Ppoint_t a, Ppoint_t b, Ppoint_t c)
Definition: visibility.c:65
format_type
#define ND_lw(n)
Definition: types.h:513
pointf Bezier(pointf *V, int degree, double t, pointf *Left, pointf *Right)
Definition: utils.c:221
#define NULL
Definition: logic.h:39
Definition: geom.h:26
double x
Definition: geom.h:28
#define ND_coord(n)
Definition: types.h:496
char just
Definition: textspan.h:65
#define alpha
Definition: shapes.c:3902
union obj_state_s::@23 u
gvplugin_installed_t gvrender_vrml_types[]
edge_t * e
Definition: gvcjob.h:198
#define M_PI
Definition: arith.h:77
#define N_GGNEW(n, t)
Definition: memory.h:40
agxbuf * str
Definition: htmlparse.c:85
double z
Definition: gvcjob.h:211
#define BEZIERSUBDIVISION
Definition: taper.c:30
gvcolor_t fillcolor
Definition: gvcjob.h:203
node_t * n
Definition: gvcjob.h:197
double penwidth
Definition: gvcjob.h:208
int y
Definition: geom.h:26
unsigned char rgba[4]
Definition: color.h:38
double tail_z
Definition: gvcjob.h:211
#define FALSE
Definition: cgraph.h:35
#define MAXDOUBLE
Definition: arith.h:64
void gvprintf(GVJ_t *job, const char *format,...)
Definition: gvdevice.c:389
textfont_t * font
Definition: textspan.h:60
Definition: geom.h:33
#define TRUE
Definition: cgraph.h:38