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
41extern shape_kind shapeOf(node_t *);
42extern pointf gvrender_ptf(GVJ_t *job, pointf p);
43extern pointf Bezier(pointf * V, int degree, double t, pointf * Left, pointf * Right);
44
45typedef enum { FORMAT_VRML, } format_type;
46
47#define BEZIERSUBDIVISION 10
48
49/* static int N_pages; */
50/* static point Pages; */
51static double Scale;
52static double MinZ;
53/* static int onetime = TRUE; */
54static int Saw_skycolor;
55
56static gdImagePtr im;
57static FILE *PNGfile;
58static int IsSegment; /* set true if edge is line segment */
59static double CylHt; /* height of cylinder part of edge */
60static double EdgeLen; /* length between centers of endpoints */
61static double HeadHt, TailHt; /* height of arrows */
62static double Fstz, Sndz; /* z values of tail and head points */
63
64/* gdirname:
65 * Returns directory pathname prefix
66 * Code adapted from dgk
67 */
68static 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
98static 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
113static 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
124static 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
139static 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
156static 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
198static 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
211static 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
232static 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
258static 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
268static 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
278static void
279finishSegment (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
320static void vrml_end_edge(GVJ_t *job)
321{
322 if (IsSegment)
323 finishSegment(job, job->obj->u.e);
324 gvputs(job, "] }\n");
325}
326
327extern void gdgen_text(gdImagePtr im, pointf spf, pointf epf, int fontcolor, double fontsize, int fontdpi, double fontangle, char *fontname, char *str);
328
329static 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 */
371static double
372interpolate_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 */
397static int
398collinear (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 */
410static int
411straight (pointf * A, int n)
412{
413 if (n != 4) return 0;
414 return (collinear(A) && collinear(A+1));
415}
416
417static void
418doSegment (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 */
456static int
457nearTail (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
468static void
469vrml_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 */
520static 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
562static 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 */
676static void
677doSphere (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
709static 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
790static 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
823static 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
831static gvdevice_features_t device_features_vrml = {
832 GVDEVICE_BINARY_FORMAT
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
840gvplugin_installed_t gvrender_vrml_types[] = {
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
847gvplugin_installed_t gvdevice_vrml_types[] = {
848#ifdef HAVE_GD_PNG
849 {FORMAT_VRML, "vrml:vrml", 1, NULL, &device_features_vrml},
850#endif
851 {0, NULL, 0, NULL, NULL}
852};
853