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