Graphviz 16.1.1~dev.20261017.1642
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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.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/overflow.h>
40#include <util/strview.h>
41#include <util/unreachable.h>
42
43/* for wind() */
44#include <pathplan/pathutil.h>
45
46enum { FORMAT_VRML, };
47
48#define BEZIERSUBDIVISION 10
49
50typedef struct {
51 double Scale;
52 double MinZ;
53 bool Saw_skycolor;
54
55 gdImagePtr im;
56 FILE *PNGfile;
57 int IsSegment; /* set true if edge is line segment */
58 double CylHt; /* height of cylinder part of edge */
59 double EdgeLen; /* length between centers of endpoints */
60 double HeadHt, TailHt; /* height of arrows */
61 double Fstz, Sndz; /* z values of tail and head points */
62} state_t;
63
64static void vrml_begin_job(GVJ_t *job) {
65 job->context = gv_alloc(sizeof(state_t));
66}
67
68static void vrml_end_job(GVJ_t *job) {
69 free(job->context);
70}
71
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) {
158 obj_state_t *obj = job->obj;
159 int i, pen, pencolor, transparent, dashstyle[20];
160
161 pen = pencolor = color_index(im, obj->pencolor);
162 transparent = gdImageGetTransparent(im);
163 if (obj->pen == PEN_DASHED) {
164 for (i = 0; i < 10; i++)
165 dashstyle[i] = pencolor;
166 for (; i < 20; i++)
167 dashstyle[i] = transparent;
168 gdImageSetStyle(im, dashstyle, 20);
169 pen = gdStyled;
170 } else if (obj->pen == PEN_DOTTED) {
171 for (i = 0; i < 2; i++)
172 dashstyle[i] = pencolor;
173 for (; i < 12; i++)
174 dashstyle[i] = transparent;
175 gdImageSetStyle(im, dashstyle, 12);
176 pen = gdStyled;
177 }
178 int width = d2i(obj->penwidth * job->scale.x);
179 if (width < PENWIDTH_NORMAL)
180 width = PENWIDTH_NORMAL; /* gd can't do thin lines */
181 gdImageSetThickness(im, width);
182 /* use brush instead of Thickness to improve end butts */
183 if (width != (int)PENWIDTH_NORMAL) {
184 gdImagePtr const brush = gdImageCreate(width, width);
185 gdImagePaletteCopy(brush, im);
186 gdImageFilledRectangle(brush, 0, 0, width - 1, width - 1, pencolor);
187 gdImageSetBrush(im, brush);
188 if (pen == gdStyled)
189 pen = gdStyledBrushed;
190 else
191 pen = gdBrushed;
192 gdImageDestroy(brush);
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/* Given 2 points in 3D p = (fst.x,fst.y,fstz) and q = (snd.x, snd.y, sndz),
373 * and a point p1 in the xy plane lying on the line segment connecting
374 * the projections of the p and q, find the z coordinate of p1 when it
375 * is projected up onto the segment (p,q) in 3-space.
376 *
377 * Why the special case for ranks? Is the arithmetic really correct?
378 */
379static double
380interpolate_zcoord(GVJ_t *job, pointf p1, pointf fst, double fstz, pointf snd, double sndz)
381{
382 obj_state_t *obj = job->obj;
383 edge_t *e = obj->u.e;
384
385 if (ND_rank(agtail(e)) != ND_rank(aghead(e))) {
386 double res;
387 if (fdiv_overflow(fstz + (sndz - fstz) * (p1.y - fst.y), snd.y - fst.y, &res))
388 return (fstz + sndz) / 2.0;
389 return res;
390 }
391 const double len = DIST(fst, snd);
392 const double d = DIST(p1, fst) / len;
393 return fstz + d * (sndz - fstz);
394}
395
397static int
398collinear (pointf * A)
399{
400 const double w = wind(A[0],A[1],A[2]);
401 return fabs(w) <= 1;
402}
403
404/* Return true if Bézier points are collinear
405 * At present, just check with 4 points, the common case.
406 */
407static int straight(pointf *A, size_t n) {
408 if (n != 4) return 0;
409 return collinear(A) && collinear(A + 1);
410}
411
412static void
413doSegment (GVJ_t *job, pointf* A, pointf p0, double z0, pointf p1, double z1)
414{
415 obj_state_t *obj = job->obj;
416 double d1, d0;
417 double delx, dely, delz;
418 state_t *state = job->context;
419
420 delx = p0.x - p1.x;
421 dely = p0.y - p1.y;
422 delz = z0 - z1;
423 state->EdgeLen = sqrt(delx*delx + dely*dely + delz*delz);
424 d0 = DIST(A[0],p0);
425 d1 = DIST(A[3],p1);
426 state->CylHt = state->EdgeLen - d0 - d1;
427 state->TailHt = state->HeadHt = 0;
428
429 state->IsSegment = 1;
430 gvputs(job, "Transform {\n"
431 " children [\n"
432 " Shape {\n"
433 " geometry Cylinder {\n"
434 " bottom FALSE top FALSE\n");
435 gvprintf(job, " height %.3f radius %.3f }\n", state->CylHt, obj->penwidth);
436 gvputs(job, " appearance Appearance {\n"
437 " material Material {\n"
438 " ambientIntensity 0.33\n");
439 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
440 obj->pencolor.u.rgba[0] / 255.,
441 obj->pencolor.u.rgba[1] / 255.,
442 obj->pencolor.u.rgba[2] / 255.);
443 gvputs(job, " }\n"
444 " }\n"
445 " }\n");
446}
447
448/* Given a point a and edge e, return true if a is closer to the
449 * tail of e than the head.
450 */
451static int
452nearTail (GVJ_t* job, pointf a, Agedge_t* e)
453{
454 pointf tp = gvrender_ptf(job, ND_coord(agtail(e)));
455 pointf hp = gvrender_ptf(job, ND_coord(aghead(e)));
456
457 return DIST2(a, tp) < DIST2(a, hp);
458}
459
460 /* this is gruesome, but how else can we get z coord */
461#define GETZ(jp,op,p,e) (nearTail(jp,p,e)?op->tail_z:op->head_z)
462
463static void vrml_bezier(GVJ_t *job, pointf *A, size_t n, int filled) {
464 (void)filled;
465
466 obj_state_t *obj = job->obj;
467 edge_t *e = obj->u.e;
468 double fstz, sndz;
469 pointf p1, V[4];
470 int step;
471 state_t *state = job->context;
472
473 assert(e);
474
475 fstz = state->Fstz = obj->tail_z;
476 sndz = state->Sndz = obj->head_z;
477 if (straight(A, n)) {
478 doSegment (job, A, gvrender_ptf(job, ND_coord(agtail(e))),state->Fstz,gvrender_ptf(job, ND_coord(aghead(e))),state->Sndz);
479 return;
480 }
481
482 gvputs(job, "Shape { geometry Extrusion {\n"
483 " spine [");
484 V[3] = A[0];
485 for (size_t i = 0; i + 3 < n; i += 3) {
486 V[0] = V[3];
487 for (size_t j = 1; j <= 3; j++)
488 V[j] = A[i + j];
489 for (step = 0; step <= BEZIERSUBDIVISION; step++) {
490 p1 = Bezier(V, (double)step / BEZIERSUBDIVISION, NULL, NULL);
491 gvprintf(job, " %.3f %.3f %.3f", p1.x, p1.y,
492 interpolate_zcoord(job, p1, A[0], fstz, A[n - 1], sndz));
493 }
494 }
495 gvputs(job, " ]\n");
496 gvprintf(job, " crossSection [ %.3f %.3f, %.3f %.3f, %.3f %.3f, %.3f %.3f ]\n",
497 obj->penwidth, obj->penwidth, -obj->penwidth,
498 obj->penwidth, -obj->penwidth, -obj->penwidth,
499 obj->penwidth, -obj->penwidth);
500 gvputs(job, "}\n");
501 gvprintf(job, " appearance DEF E%d Appearance {\n", AGSEQ(e));
502 gvputs(job, " material Material {\n"
503 " ambientIntensity 0.33\n");
504 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
505 obj->pencolor.u.rgba[0] / 255.,
506 obj->pencolor.u.rgba[1] / 255.,
507 obj->pencolor.u.rgba[2] / 255.);
508 gvputs(job, " }\n"
509 " }\n"
510 "}\n");
511}
512
514static void doArrowhead (GVJ_t *job, pointf * A)
515{
516 obj_state_t *obj = job->obj;
517 edge_t *e = obj->u.e;
518 double rad, ht, y;
519 pointf p0; /* center of triangle base */
520 state_t *state = job->context;
521
522 p0.x = (A[0].x + A[2].x)/2.0;
523 p0.y = (A[0].y + A[2].y)/2.0;
524 rad = DIST(A[0],A[2])/2.0;
525 ht = DIST(p0,A[1]);
526
527 y = (state->CylHt + ht)/2.0;
528
529 gvputs(job, "Transform {\n");
530 if (nearTail (job, A[1], e)) {
531 state->TailHt = ht;
532 gvprintf(job, " translation 0 %.3f 0\n", -y);
533 gvprintf(job, " rotation 0 0 1 %.3f\n", M_PI);
534 }
535 else {
536 state->HeadHt = ht;
537 gvprintf(job, " translation 0 %.3f 0\n", y);
538 }
539 gvputs(job, " children [\n"
540 " Shape {\n");
541 gvprintf(job, " geometry Cone {bottomRadius %.3f height %.3f }\n",
542 rad, ht);
543 gvputs(job, " appearance Appearance {\n"
544 " material Material {\n"
545 " ambientIntensity 0.33\n");
546 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
547 obj->pencolor.u.rgba[0] / 255.,
548 obj->pencolor.u.rgba[1] / 255.,
549 obj->pencolor.u.rgba[2] / 255.);
550 gvputs(job, " }\n"
551 " }\n"
552 " }\n"
553 " ]\n"
554 "}\n");
555}
556
557static void vrml_polygon(GVJ_t *job, pointf *A, size_t np, int filled) {
558 obj_state_t *obj = job->obj;
559 node_t *n;
560 edge_t *e;
561 double z = obj->z;
562 pointf p, mp;
563 gdPoint *points;
564 int pen;
565 double theta;
566 state_t *state = job->context;
567
568 switch (obj->type) {
570 gvprintf(job, " Background { skyColor %.3f %.3f %.3f }\n",
571 obj->fillcolor.u.rgba[0] / 255.,
572 obj->fillcolor.u.rgba[1] / 255.,
573 obj->fillcolor.u.rgba[2] / 255.);
574 state->Saw_skycolor = true;
575 break;
576 case CLUSTER_OBJTYPE:
577 break;
578 case NODE_OBJTYPE:
579 n = obj->u.n;
580 pen = set_penstyle(job, state->im);
581 points = gv_calloc(np, sizeof(gdPoint));
582 for (size_t i = 0; i < np; i++) {
583 mp = vrml_node_point(job, n, A[i]);
584 points[i].x = ROUND(mp.x);
585 points[i].y = ROUND(mp.y);
586 }
587 assert(np <= INT_MAX);
588 if (filled)
589 gdImageFilledPolygon(state->im, points, (int)np, color_index(state->im, obj->fillcolor));
590 gdImagePolygon(state->im, points, (int)np, pen);
591 free(points);
592
593 gvputs(job, "Shape {\n"
594 " appearance Appearance {\n"
595 " material Material {\n"
596 " ambientIntensity 0.33\n"
597 " diffuseColor 1 1 1\n"
598 " }\n");
599 gvprintf(job, " texture ImageTexture { url \"node%d.png\" }\n", AGSEQ(n));
600 gvputs(job, " }\n"
601 " geometry Extrusion {\n"
602 " crossSection [");
603 for (size_t i = 0; i < np; i++) {
604 p.x = A[i].x - ND_coord(n).x;
605 p.y = A[i].y - ND_coord(n).y;
606 gvprintf(job, " %.3f %.3f,", p.x, p.y);
607 }
608 p.x = A[0].x - ND_coord(n).x;
609 p.y = A[0].y - ND_coord(n).y;
610 gvprintf(job, " %.3f %.3f ]\n", p.x, p.y);
611 gvprintf(job, " spine [ %.5g %.5g %.5g, %.5g %.5g %.5g ]\n",
612 ND_coord(n).x, ND_coord(n).y, z - .01,
613 ND_coord(n).x, ND_coord(n).y, z + .01);
614 gvputs(job, " }\n"
615 "}\n");
616 break;
617 case EDGE_OBJTYPE:
618 e = obj->u.e;
619 if (np != 3) {
620 static atomic_flag flag;
621 if (!atomic_flag_test_and_set(&flag)) {
623 "vrml_polygon: non-triangle arrowheads not supported - ignoring\n");
624 }
625 }
626 if (state->IsSegment) {
627 doArrowhead (job, A);
628 return;
629 }
630 p.x = p.y = 0.0;
631 for (size_t i = 0; i < np; i++) {
632 p.x += A[i].x;
633 p.y += A[i].y;
634 }
635 p.x /= (int)np;
636 p.y /= (int)np;
637
638 /* it is bad to know that A[1] is the aiming point, but we do */
639 theta =
640 atan2((A[0].y + A[2].y) / 2.0 - A[1].y,
641 (A[0].x + A[2].x) / 2.0 - A[1].x) + M_PI / 2.0;
642
643 z = GETZ(job,obj,p,e);
644
645 /* FIXME: arrow vector ought to follow z coord of Bézier */
646 gvputs(job, "Transform {\n");
647 gvprintf(job, " translation %.3f %.3f %.3f\n", p.x, p.y, z);
648 gvputs(job, " children [\n"
649 " Transform {\n");
650 gvprintf(job, " rotation 0 0 1 %.3f\n", theta);
651 gvputs(job, " children [\n"
652 " Shape {\n");
653 gvprintf(job, " geometry Cone {bottomRadius %.3f height %.3f }\n",
654 obj->penwidth * 2.5, obj->penwidth * 10.0);
655 gvprintf(job, " appearance USE E%d\n", AGSEQ(e));
656 gvputs(job, " }\n"
657 " ]\n"
658 " }\n"
659 " ]\n"
660 "}\n");
661 break;
662 default:
663 UNREACHABLE();
664 }
665}
666
668static void doSphere(GVJ_t *job, pointf p, double z, double rx) {
669 obj_state_t *obj = job->obj;
670
671 gvputs(job, "Transform {\n");
672 gvprintf(job, " translation %.3f %.3f %.3f\n", p.x, p.y, z);
673 gvprintf(job, " scale %.3f %.3f %.3f\n", rx, rx, rx);
674 gvputs(job, " children [\n"
675 " Transform {\n"
676 " children [\n"
677 " Shape {\n"
678 " geometry Sphere { radius 1.0 }\n"
679 " appearance Appearance {\n"
680 " material Material {\n"
681 " ambientIntensity 0.33\n");
682 gvprintf(job, " diffuseColor %.3f %.3f %.3f\n",
683 obj->pencolor.u.rgba[0] / 255.,
684 obj->pencolor.u.rgba[1] / 255.,
685 obj->pencolor.u.rgba[2] / 255.);
686 gvputs(job, " }\n"
687 " }\n"
688 " }\n"
689 " ]\n"
690 " }\n"
691 " ]\n"
692 "}\n");
693}
694
695static void vrml_ellipse(GVJ_t * job, pointf * A, int filled)
696{
697 obj_state_t *obj = job->obj;
698 node_t *n;
699 edge_t *e;
700 double z = obj->z;
701 double rx, ry;
702 int dx, dy;
703 pointf npf, nqf;
704 point np;
705 int pen;
706 state_t *state = job->context;
707
708 rx = A[1].x - A[0].x;
709 ry = A[1].y - A[0].y;
710
711 switch (obj->type) {
713 case CLUSTER_OBJTYPE:
714 break;
715 case NODE_OBJTYPE:
716 n = obj->u.n;
717 if (shapeOf(n) == SH_POINT) {
718 doSphere(job, A[0], z, rx);
719 return;
720 }
721 pen = set_penstyle(job, state->im);
722
723 npf = vrml_node_point(job, n, A[0]);
724 nqf = vrml_node_point(job, n, A[1]);
725
726 dx = ROUND(2 * (nqf.x - npf.x));
727 dy = ROUND(2 * (nqf.y - npf.y));
728
729 PF2P(npf, np);
730
731 if (filled)
732 gdImageFilledEllipse(state->im, np.x, np.y, dx, dy, color_index(state->im, obj->fillcolor));
733 gdImageArc(state->im, np.x, np.y, dx, dy, 0, 360, pen);
734
735 gvputs(job, "Transform {\n");
736 gvprintf(job, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z);
737 gvprintf(job, " scale %.3f %.3f 1\n", rx, ry);
738 gvputs(job, " children [\n"
739 " Transform {\n"
740 " rotation 1 0 0 1.57\n"
741 " children [\n"
742 " Shape {\n"
743 " geometry Cylinder { side FALSE }\n"
744 " appearance Appearance {\n"
745 " material Material {\n"
746 " ambientIntensity 0.33\n"
747 " diffuseColor 1 1 1\n"
748 " }\n");
749 gvprintf(job, " texture ImageTexture { url \"node%d.png\" }\n", AGSEQ(n));
750 gvputs(job, " }\n"
751 " }\n"
752 " ]\n"
753 " }\n"
754 " ]\n"
755 "}\n");
756 break;
757 case EDGE_OBJTYPE:
758 e = obj->u.e;
759 z = GETZ(job,obj,A[0],e);
760
761 gvputs(job, "Transform {\n");
762 gvprintf(job, " translation %.3f %.3f %.3f\n", A[0].x, A[0].y, z);
763 gvputs(job, " children [\n"
764 " Shape {\n");
765 gvprintf(job, " geometry Sphere {radius %.3f }\n", rx);
766 gvprintf(job, " appearance USE E%d\n", AGSEQ(e));
767 gvputs(job, " }\n"
768 " ]\n"
769 "}\n");
770 break;
771 default:
772 UNREACHABLE();
773 }
774}
775
776static gvrender_engine_t vrml_engine = {
777 vrml_begin_job,
778 vrml_end_job,
779 0, /* vrml_begin_graph */
780 0, /* vrml_end_graph */
781 0, /* vrml_begin_layer */
782 0, /* vrml_end_layer */
783 vrml_begin_page,
784 vrml_end_page,
785 0, /* vrml_begin_cluster */
786 0, /* vrml_end_cluster */
787 0, /* vrml_begin_nodes */
788 0, /* vrml_end_nodes */
789 0, /* vrml_begin_edges */
790 0, /* vrml_end_edges */
791 vrml_begin_node,
792 vrml_end_node,
793 vrml_begin_edge,
794 vrml_end_edge,
795 0, /* vrml_begin_anchor */
796 0, /* vrml_end_anchor */
797 0, /* vrml_begin_label */
798 0, /* vrml_end_label */
799 vrml_textspan,
800 0, /* vrml_resolve_color */
801 vrml_ellipse,
802 vrml_polygon,
803 vrml_bezier,
804 0, /* vrml_polyline - FIXME */
805 0, /* vrml_comment */
806 0, /* vrml_library_shape */
807};
808
809static gvrender_features_t render_features_vrml = {
810 GVRENDER_DOES_Z, /* flags */
811 0., /* default pad - graph units */
812 NULL, /* knowncolors */
813 0, /* sizeof knowncolors */
814 RGBA_BYTE, /* color_type */
815};
816
817static gvdevice_features_t device_features_vrml = {
819 | GVDEVICE_NO_WRITER, /* flags */
820 {0.,0.}, /* default margin - points */
821 {0.,0.}, /* default page width, height - points */
822 {72.,72.}, /* default dpi */
823};
824#endif /* HAVE_GD_PNG */
825
827#ifdef HAVE_GD_PNG
828 {FORMAT_VRML, "vrml", 1, &vrml_engine, &render_features_vrml},
829#endif
830 {0, NULL, 0, NULL, NULL}
831};
832
834#ifdef HAVE_GD_PNG
835 {FORMAT_VRML, "vrml:vrml", 1, NULL, &device_features_vrml},
836#endif
837 {0, NULL, 0, NULL, NULL}
838};
static agxbuf last
last message
Definition agerror.c:31
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:175
static float dy
Definition draw.c:43
static float dx
Definition draw.c:42
#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:138
void free(void *)
node NULL
Definition grammar.y:181
#define agtail(e)
Definition cgraph.h:982
#define aghead(e)
Definition cgraph.h:983
void agwarningf(const char *fmt,...)
Definition agerror.c:175
void agerrorf(const char *fmt,...)
Definition agerror.c:167
#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:145
#define AGSEQ(obj)
Definition cgraph.h:225
Arithmetic helper functions.
static int d2i(double v)
Definition gv_math.h:153
@ 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:118
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
arithmetic overflow helpers
static bool fdiv_overflow(double n, double d, double *res)
Definition overflow.h:142
PATHUTIL_API int wind(Ppoint_t a, Ppoint_t b, Ppoint_t c)
Definition visibility.c:55
shape_kind shapeOf(node_t *)
Definition shapes.c:1909
#define alpha
Definition shapes.c:4034
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
union obj_state_s::@65 u
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:29
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:59
char just
'l' 'n' 'r'
Definition textspan.h:74
pointf size
Definition textspan.h:73
textfont_t * font
Definition textspan.h:69
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