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