Graphviz 13.0.0~dev.20250210.0415
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 o_x, o_y, o_z;
292 double x, y, y0, z, theta;
293 state_t *state = job->context;
294
295 o_x = ((double)(p0.x + p1.x))/2;
296 o_y = ((double)(p0.y + p1.y))/2;
297 o_z = (state->Fstz + state->Sndz) / 2;
298 /* Compute rotation */
299 /* Pick end point with highest y */
300 if (p0.y > p1.y) {
301 x = p0.x;
302 y = p0.y;
303 z = state->Fstz;
304 }
305 else {
306 x = p1.x;
307 y = p1.y;
308 z = state->Sndz;
309 }
310 /* Translate center to the origin */
311 x -= o_x;
312 y -= o_y;
313 z -= o_z;
314 if (p0.y > p1.y)
315 theta = acos(2 * y / state->EdgeLen) + M_PI;
316 else
317 theta = acos(2 * y / state->EdgeLen);
318 if (!x && !z) /* parallel to y-axis */
319 x = 1;
320
321 y0 = (state->HeadHt - state->TailHt) / 2.0;
322 gvputs(job, " ]\n");
323 gvprintf(job, " center 0 %.3f 0\n", y0);
324 gvprintf(job, " rotation %.3f 0 %.3f %.3f\n", -z, x, -theta);
325 gvprintf(job, " translation %.3f %.3f %.3f\n", o_x, o_y - y0, o_z);
326 gvputs(job, " }\n");
327}
328
329static void vrml_end_edge(GVJ_t *job)
330{
331 state_t *state = job->context;
332
333 if (state->IsSegment)
334 finishSegment(job, job->obj->u.e);
335 gvputs(job, "] }\n");
336}
337
338static void vrml_textspan(GVJ_t *job, pointf p, textspan_t * span)
339{
340 obj_state_t *obj = job->obj;
341 pointf spf, epf, q;
342 state_t *state = job->context;
343
344 if (!obj->u.n || !state->im) /* if not a node - or if no im (e.g. for cluster) */
345 return;
346
347 switch (span->just) {
348 case 'l':
349 break;
350 case 'r':
351 p.x -= span->size.x;
352 break;
353 default:
354 case 'n':
355 p.x -= span->size.x / 2;
356 break;
357 }
358 q.x = p.x + span->size.x;
359 q.y = p.y;
360
361 spf = vrml_node_point(job, obj->u.n, p);
362 epf = vrml_node_point(job, obj->u.n, q);
363
364 gdgen_text(state->im, spf, epf,
365 color_index(state->im, obj->pencolor),
366 span->font->size,
367 DEFAULT_DPI,
368 job->rotation ? (M_PI / 2) : 0,
369 span->font->name,
370 span->str);
371}
372
373/* interpolate_zcoord:
374 * Given 2 points in 3D p = (fst.x,fst.y,fstz) and q = (snd.x, snd.y, sndz),
375 * and a point p1 in the xy plane lying on the line segment connecting
376 * the projections of the p and q, find the z coordinate of p1 when it
377 * is projected up onto the segment (p,q) in 3-space.
378 *
379 * Why the special case for ranks? Is the arithmetic really correct?
380 */
381static double
382interpolate_zcoord(GVJ_t *job, pointf p1, pointf fst, double fstz, pointf snd, double sndz)
383{
384 obj_state_t *obj = job->obj;
385 edge_t *e = obj->u.e;
386 double len, d, rv;
387
388 if (fstz == sndz)
389 return fstz;
390 if (ND_rank(agtail(e)) != ND_rank(aghead(e))) {
391 if (snd.y == fst.y)
392 rv = (fstz + sndz) / 2.0;
393 else
394 rv = fstz + (sndz - fstz) * (p1.y - fst.y) / (snd.y - fst.y);
395 }
396 else {
397 len = DIST(fst, snd);
398 d = DIST(p1, fst)/len;
399 rv = fstz + d*(sndz - fstz);
400 }
401 return rv;
402}
403
404/* collinear:
405 * Return true if the 3 points starting at A are collinear.
406 */
407static int
408collinear (pointf * A)
409{
410 double w;
411
412 w = wind(A[0],A[1],A[2]);
413 return fabs(w) <= 1;
414}
415
416/* straight:
417 * Return true if bezier points are collinear
418 * At present, just check with 4 points, the common case.
419 */
420static int straight(pointf *A, size_t n) {
421 if (n != 4) return 0;
422 return collinear(A) && collinear(A + 1);
423}
424
425static void
426doSegment (GVJ_t *job, pointf* A, pointf p0, double z0, pointf p1, double z1)
427{
428 obj_state_t *obj = job->obj;
429 double d1, d0;
430 double delx, dely, delz;
431 state_t *state = job->context;
432
433 delx = p0.x - p1.x;
434 dely = p0.y - p1.y;
435 delz = z0 - z1;
436 state->EdgeLen = sqrt(delx*delx + dely*dely + delz*delz);
437 d0 = DIST(A[0],p0);
438 d1 = DIST(A[3],p1);
439 state->CylHt = state->EdgeLen - d0 - d1;
440 state->TailHt = state->HeadHt = 0;
441
442 state->IsSegment = 1;
443 gvputs(job, "Transform {\n"
444 " children [\n"
445 " Shape {\n"
446 " geometry Cylinder {\n"
447 " bottom FALSE top FALSE\n");
448 gvprintf(job, " height %.3f radius %.3f }\n", state->CylHt, obj->penwidth);
449 gvputs(job, " appearance Appearance {\n"
450 " material Material {\n"
451 " ambientIntensity 0.33\n");
452 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
453 obj->pencolor.u.rgba[0] / 255.,
454 obj->pencolor.u.rgba[1] / 255.,
455 obj->pencolor.u.rgba[2] / 255.);
456 gvputs(job, " }\n"
457 " }\n"
458 " }\n");
459}
460
461/* nearTail:
462 * Given a point a and edge e, return true if a is closer to the
463 * tail of e than the head.
464 */
465static int
466nearTail (GVJ_t* job, pointf a, Agedge_t* e)
467{
468 pointf tp = gvrender_ptf(job, ND_coord(agtail(e)));
469 pointf hp = gvrender_ptf(job, ND_coord(aghead(e)));
470
471 return (DIST2(a, tp) < DIST2(a, hp));
472}
473
474 /* this is gruesome, but how else can we get z coord */
475#define GETZ(jp,op,p,e) (nearTail(jp,p,e)?op->tail_z:op->head_z)
476
477static void vrml_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
478 (void)filled;
479
480 obj_state_t *obj = job->obj;
481 edge_t *e = obj->u.e;
482 double fstz, sndz;
483 pointf p1, V[4];
484 int step;
485 state_t *state = job->context;
486
487 assert(e);
488
489 fstz = state->Fstz = obj->tail_z;
490 sndz = state->Sndz = obj->head_z;
491 if (straight(A, n)) {
492 doSegment (job, A, gvrender_ptf(job, ND_coord(agtail(e))),state->Fstz,gvrender_ptf(job, ND_coord(aghead(e))),state->Sndz);
493 return;
494 }
495
496 gvputs(job, "Shape { geometry Extrusion {\n"
497 " spine [");
498 V[3] = A[0];
499 for (size_t i = 0; i + 3 < n; i += 3) {
500 V[0] = V[3];
501 for (size_t j = 1; j <= 3; j++)
502 V[j] = A[i + j];
503 for (step = 0; step <= BEZIERSUBDIVISION; step++) {
504 p1 = Bezier(V, (double)step / BEZIERSUBDIVISION, NULL, NULL);
505 gvprintf(job, " %.3f %.3f %.3f", p1.x, p1.y,
506 interpolate_zcoord(job, p1, A[0], fstz, A[n - 1], sndz));
507 }
508 }
509 gvputs(job, " ]\n");
510 gvprintf(job, " crossSection [ %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f ]\n",
511 (obj->penwidth), (obj->penwidth), -(obj->penwidth),
512 (obj->penwidth), -(obj->penwidth), -(obj->penwidth),
513 (obj->penwidth), -(obj->penwidth));
514 gvputs(job, "}\n");
515 gvprintf(job, " appearance DEF E%d Appearance {\n", AGSEQ(e));
516 gvputs(job, " material Material {\n"
517 " ambientIntensity 0.33\n");
518 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
519 obj->pencolor.u.rgba[0] / 255.,
520 obj->pencolor.u.rgba[1] / 255.,
521 obj->pencolor.u.rgba[2] / 255.);
522 gvputs(job, " }\n"
523 " }\n"
524 "}\n");
525}
526
527/* doArrowhead:
528 * If edge is straight, we attach a cone to the edge as a group.
529 */
530static void doArrowhead (GVJ_t *job, pointf * A)
531{
532 obj_state_t *obj = job->obj;
533 edge_t *e = obj->u.e;
534 double rad, ht, y;
535 pointf p0; /* center of triangle base */
536 state_t *state = job->context;
537
538 p0.x = (A[0].x + A[2].x)/2.0;
539 p0.y = (A[0].y + A[2].y)/2.0;
540 rad = DIST(A[0],A[2])/2.0;
541 ht = DIST(p0,A[1]);
542
543 y = (state->CylHt + ht)/2.0;
544
545 gvputs(job, "Transform {\n");
546 if (nearTail (job, A[1], e)) {
547 state->TailHt = ht;
548 gvprintf(job, " translation 0 %.3f 0\n", -y);
549 gvprintf(job, " rotation 0 0 1 %.3f\n", M_PI);
550 }
551 else {
552 state->HeadHt = ht;
553 gvprintf(job, " translation 0 %.3f 0\n", y);
554 }
555 gvputs(job, " children [\n"
556 " Shape {\n");
557 gvprintf(job, " geometry Cone {bottomRadius %.3f height %.3f }\n",
558 rad, ht);
559 gvputs(job, " appearance Appearance {\n"
560 " material Material {\n"
561 " ambientIntensity 0.33\n");
562 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
563 obj->pencolor.u.rgba[0] / 255.,
564 obj->pencolor.u.rgba[1] / 255.,
565 obj->pencolor.u.rgba[2] / 255.);
566 gvputs(job, " }\n"
567 " }\n"
568 " }\n"
569 " ]\n"
570 "}\n");
571}
572
573static void vrml_polygon(GVJ_t *job, pointf *A, size_t np, int filled) {
574 obj_state_t *obj = job->obj;
575 node_t *n;
576 edge_t *e;
577 double z = obj->z;
578 pointf p, mp;
579 gdPoint *points;
580 int pen;
581 gdImagePtr brush = NULL;
582 double theta;
583 state_t *state = job->context;
584
585 switch (obj->type) {
587 gvprintf(job, " Background { skyColor %.3f %.3f %.3f }\n",
588 obj->fillcolor.u.rgba[0] / 255.,
589 obj->fillcolor.u.rgba[1] / 255.,
590 obj->fillcolor.u.rgba[2] / 255.);
591 state->Saw_skycolor = true;
592 break;
593 case CLUSTER_OBJTYPE:
594 break;
595 case NODE_OBJTYPE:
596 n = obj->u.n;
597 pen = set_penstyle(job, state->im, brush);
598 points = gv_calloc(np, sizeof(gdPoint));
599 for (size_t i = 0; i < np; i++) {
600 mp = vrml_node_point(job, n, A[i]);
601 points[i].x = ROUND(mp.x);
602 points[i].y = ROUND(mp.y);
603 }
604 assert(np <= INT_MAX);
605 if (filled)
606 gdImageFilledPolygon(state->im, points, (int)np, color_index(state->im, obj->fillcolor));
607 gdImagePolygon(state->im, points, (int)np, pen);
608 free(points);
609 if (brush)
610 gdImageDestroy(brush);
611
612 gvputs(job, "Shape {\n"
613 " appearance Appearance {\n"
614 " material Material {\n"
615 " ambientIntensity 0.33\n"
616 " diffuseColor 1 1 1\n"
617 " }\n");
618 gvprintf(job, " texture ImageTexture { url \"node%d.png\" }\n", AGSEQ(n));
619 gvputs(job, " }\n"
620 " geometry Extrusion {\n"
621 " crossSection [");
622 for (size_t i = 0; i < np; i++) {
623 p.x = A[i].x - ND_coord(n).x;
624 p.y = A[i].y - ND_coord(n).y;
625 gvprintf(job, " %.3f %.3f,", p.x, p.y);
626 }
627 p.x = A[0].x - ND_coord(n).x;
628 p.y = A[0].y - ND_coord(n).y;
629 gvprintf(job, " %.3f %.3f ]\n", p.x, p.y);
630 gvprintf(job, " spine [ %.5g %.5g %.5g, %.5g %.5g %.5g ]\n",
631 ND_coord(n).x, ND_coord(n).y, z - .01,
632 ND_coord(n).x, ND_coord(n).y, z + .01);
633 gvputs(job, " }\n"
634 "}\n");
635 break;
636 case EDGE_OBJTYPE:
637 e = obj->u.e;
638 if (np != 3) {
639 static atomic_flag flag;
640 if (!atomic_flag_test_and_set(&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:78
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:234
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
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:170
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: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:150
void free(void *)
node NULL
Definition grammar.y:163
#define agtail(e)
Definition cgraph.h:883
#define aghead(e)
Definition cgraph.h:884
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:264
void gvprintf(GVJ_t *job, const char *format,...)
Definition gvdevice.c:395
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:224
$2 font
Definition htmlparse.y:300
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::@71 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
union obj_state_s::@89 u
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