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