From: ellson Date: Tue, 20 Jun 2006 19:01:06 +0000 (+0000) Subject: first cut at vrml plugin - not buildable yet X-Git-Tag: LAST_LIBGRAPH~32^2~6391 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d37109b7e82ab4ef0880bb1eb8068f865a82ffd1;p=graphviz first cut at vrml plugin - not buildable yet --- diff --git a/plugin/gd/gvrender_gd_vrml.c b/plugin/gd/gvrender_gd_vrml.c new file mode 100644 index 000000000..eae7614d3 --- /dev/null +++ b/plugin/gd/gvrender_gd_vrml.c @@ -0,0 +1,1037 @@ +/* $Id$ $Revision$ */ +/* vim:set shiftwidth=4 ts=8: */ + +/********************************************************** +* This software is part of the graphviz package * +* http://www.graphviz.org/ * +* * +* Copyright (c) 1994-2004 AT&T Corp. * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Corp. * +* * +* Information and Software Systems Research * +* AT&T Research, Florham Park NJ * +**********************************************************/ + + +#include "render.h" +#include "gd.h" +#include "pathutil.h" + +extern char *get_ttf_fontpath(char *fontreq, int warn); + +#ifdef HAVE_GD_PNG + +typedef enum { FORMAT_VRML, } format_type; + +#ifndef MAXFLOAT +#define MAXFLOAT 10000000. +#endif + +#define NONE 0 +#define NODE 1 +#define EDGE 2 +#define CLST 3 + +#define BEZIERSUBDIVISION 10 + +/* font modifiers */ +#define REGULAR 0 +#define BOLD 1 +#define ITALIC 2 + +/* patterns */ +#define P_SOLID 0 +#define P_NONE 15 +#define P_DOTTED 4 /* i wasn't sure about this */ +#define P_DASHED 11 /* or this */ + +/* bold line constant */ +#define WIDTH_NORMAL 1 +#define WIDTH_BOLD 3 + +typedef struct { + unsigned char r, g, b; +} Color; + + +/* static int N_pages; */ +/* static point Pages; */ +static double Scale; +static int Rot; +static box BB; +static double MinZ; +/* static int onetime = TRUE; */ +static int Saw_skycolor; + +static gdImagePtr im; +static FILE *PNGfile; +static int IsSegment; /* set true if edge is line segment */ +static double CylHt; /* height of cylinder part of edge */ +static double EdgeLen; /* length between centers of endpoints */ +static double HeadHt, TailHt; /* height of arrows */ +static double Fstz, Sndz; /* z values of tail and head points */ + +typedef struct context_t { + unsigned char pencolor_ix, fillcolor_ix; + char *pencolor, *fillcolor; + char *fontfam, fontopt, font_was_set; + double r, g, b; /* fill color values */ + char pen, fill, penwidth; + double fontsz; +} context_t; + +#define MAXNEST 4 +static context_t cstk[MAXNEST]; +static int SP; + +/* gdirname: + * Returns directory pathname prefix + * Code adapted from dgk + */ +static char *gdirname(char *pathname) +{ + char *last; + + /* go to end of path */ + for (last = pathname; *last; last++); + /* back over trailing '/' */ + while (last > pathname && *--last == '/'); + /* back over non-slash chars */ + for (; last > pathname && *last != '/'; last--); + if (last == pathname) { + /* all '/' or "" */ + if (*pathname != '/') + *last = '.'; + /* preserve // */ + else if (pathname[1] == '/') + last++; + } else { + /* back over trailing '/' */ + for (; *last == '/' && last > pathname; last--); + /* preserve // */ + if (last == pathname && *pathname == '/' && pathname[1] == '/') + last++; + } + last++; + *last = '\0'; + + return pathname; +} + +static char *nodefilename(char *filename, node_t * n, char *buf) +{ + static char *dir; + static char disposable[1024]; + + if (dir == 0) { + if (filename) + dir = gdirname(strcpy(disposable, filename)); + else + dir = "."; + } + sprintf(buf, "%s/node%d.png", dir, n->id); + return buf; +} + +static FILE *nodefile(char *filename, node_t * n) +{ + FILE *rv; + char buf[1024]; + + rv = fopen(nodefilename(filename, n, buf), "wb"); + return rv; +} + +static unsigned char vrml_resolve_color(char *name) +{ + gvcolor_t color; + + if (!(strcmp(name, "transparent"))) { + /* special case for "transparent" color */ + return gdImageColorResolve(im, 255, 255, 254); + } else { + colorxlate(name, &color, RGBA_BYTE); + return gdImageColorResolve(im, + color.u.rgba[0], color.u.rgba[1], + color.u.rgba[2]); + } +} + +static void vrml_set_pencolor(char *name) +{ + cstk[SP].pencolor = name; +} + +static void vrml_set_fillcolor(char *name) +{ + gvcolor_t color; + cstk[SP].fillcolor = name; + colorxlate(name, &color, RGBA_BYTE); + cstk[SP].r = (double) color.u.rgba[0] / 255.0; + cstk[SP].g = (double) color.u.rgba[1] / 255.0; + cstk[SP].b = (double) color.u.rgba[2] / 255.0; +} + +static void init_png(gdImagePtr im) +{ + int transparent; + + if ((transparent = gdImageGetTransparent(im)) == -1) { + transparent = gdImageColorResolve(im, 255, 255, 254); + gdImageColorTransparent(im, transparent); + } +} + +static pointf vrml_node_point(node_t *n, point p) +{ + pointf rv; + + /* make mp relative to PNG canvas */ + if (Rot == 0) { + rv.x = (p.x - ND_coord_i(n).x + ND_lw_i(n)) * Scale; + rv.y = (ND_coord_i(n).y - p.y + ND_ht_i(n) / 2) * Scale; + } else { + rv.x = (p.y - ND_coord_i(n).y + ND_lw_i(n)) * Scale; + rv.y = (ND_coord_i(n).x - p.x + ND_ht_i(n) / 2) * Scale; + } + return rv; +} + +static void vrml_font(context_t * cp) +{ +/* FIX + char *fw, *fa; + + fw = fa = "Regular"; + switch (cp->fontopt) { + case BOLD: + fw = "Bold"; + break; + case ITALIC: + fa = "Italic"; + break; + } +*/ +} + +/* warmed over VRML code starts here */ + +static void vrml_begin_page(GVJ_t *job) +{ + FILE *out = job->output_file; + +#if 0 /* scale not used */ + Scale = scale * (double) DEFAULT_DPI / POINTS_PER_INCH; +#else + Scale = (double) DEFAULT_DPI / POINTS_PER_INCH; +#endif + Rot = rot; + + fprintf(out, "#VRML V2.0 utf8\n"); + + Saw_skycolor = FALSE; + MinZ = MAXDOUBLE; + BB = bb; + fprintf(out, "Group { children [\n"); + fprintf(out, " Transform {\n"); + fprintf(out, " scale %.3f %.3f %.3f\n", .0278, .0278, .0278); + fprintf(out, " children [\n"); + + SP = 0; + cstk[0].fillcolor = "white"; + cstk[0].fontfam = "times"; /* font family name */ + cstk[0].fontopt = REGULAR; /* modifier: REGULAR, BOLD or ITALIC */ + cstk[0].pen = P_SOLID; /* pen pattern style, default is solid */ + cstk[0].fill = P_NONE; + cstk[0].penwidth = WIDTH_NORMAL; +} + +static void vrml_end_page(GVJ_t *job) + FILE *out = job->output_file; + double d, z; + box bb = BB; + + d = MAX(bb.UR.x - bb.LL.x,bb.UR.y - bb.LL.y); + /* Roughly fill 3/4 view assuming FOV angle of PI/4. + * Small graphs and non-square aspect ratios will upset this. + */ + z = (0.6667*d)/tan(PI/8.0) + MinZ; /* fill 3/4 of view */ + + if (!Saw_skycolor) + fprintf(out, " Background { skyColor 1 1 1 }\n"); + fprintf(out, " ] }\n"); + fprintf(out, " Viewpoint {position %.3f %.3f %.3f}\n", + .0278 * (bb.UR.x + bb.LL.x) / 2.0, + .0278 * (bb.UR.y + bb.LL.y) / 2.0, .0278 * z); + fprintf(out, "] }\n"); +} + +static void vrml_begin_node(GVJ_t *job) +{ + FILE *out = job->output_file; + obj_state_t *obj = job->obj; + node_t *n = obj->n; + int width, height; + double z; + + fprintf(out, "# node %s\n", n->name); + z = late_double(n, N_z, 0.0, -MAXFLOAT); + if (z < MinZ) MinZ = z; + if (shapeOf(n) != SH_POINT) { + PNGfile = nodefile(job->output_file_name, n); + width = (ND_lw_i(n) + ND_rw_i(n)) * Scale + 3; + height = (ND_ht_i(n)) * Scale + 3; + im = gdImageCreate(width, height); + init_png(im); + } +} + +static void vrml_end_node(GVJ_t *job) +{ + obj_state_t *obj = job->obj; + node_t *n = obj->n; + + if (shapeOf(n) != SH_POINT) { + gdImagePng(im, PNGfile); + gdImageDestroy(im); + im = 0; + fclose(PNGfile); + } +} + +static void vrml_begin_edge(GVJ_t *job) +{ + FILE *out = job->output_file; + edge_t *e = job->obj->e; + + IsSegment = 0; + fprintf(out, "# edge %s -> %s\n", e->tail->name, e->head->name); + fprintf(out, " Group { children [\n"); +} + +static void +finishSegment (FILE *out, edge_t *e) +{ + point p0 = ND_coord_i(e->tail); + point p1 = ND_coord_i(e->head); + double o_x, o_y, o_z; + double x, y, y0, z, theta; + + o_x = ((double)(p0.x + p1.x))/2; + o_y = ((double)(p0.y + p1.y))/2; + o_z = (Fstz + Sndz)/2; + /* Compute rotation */ + /* Pick end point with highest y */ + if (p0.y > p1.y) { + x = p0.x; + y = p0.y; + z = Fstz; + } + else { + x = p1.x; + y = p1.y; + z = Sndz; + } + /* Translate center to the origin */ + x -= o_x; + y -= o_y; + z -= o_z; + if (p0.y > p1.y) + theta = acos(2*y/EdgeLen) + PI; + else + theta = acos(2*y/EdgeLen); + if (!x && !z) /* parallel to y-axis */ + x = 1; + + y0 = (HeadHt-TailHt)/2.0; + fprintf(out, " ]\n"); + fprintf(out, " center 0 %f 0\n", y0); + fprintf(out, " rotation %f 0 %f %f\n", -z, x, -theta); + fprintf(out, " translation %.3f %.3f %.3f\n", o_x, o_y - y0, o_z); + fprintf(out, " }\n"); +} + +static void vrml_end_edge(GVJ_t *job) +{ + if (IsSegment) + finishSegment(job->output_file, job->obj->e); + fprintf(job->output_file, "] }\n"); +} + +static void vrml_begin_context(void) +{ + assert(SP + 1 < MAXNEST); + cstk[SP + 1] = cstk[SP]; + SP++; +} + +static void vrml_end_context(void) +{ + int psp = SP - 1; + assert(SP > 0); + if (cstk[SP].font_was_set) + vrml_font(&(cstk[psp])); + /* free(cstk[psp].fontfam); */ + SP = psp; +} + +static void vrml_set_font(char *name, double size) +{ + char *p, *q; + context_t *cp; + + cp = &(cstk[SP]); + cp->font_was_set = TRUE; + cp->fontsz = size; + p = strdup(name); + if ((q = strchr(p, '-'))) { + *q++ = 0; + if (strcasecmp(q, "italic") == 0) + cp->fontopt = ITALIC; + else if (strcasecmp(q, "bold") == 0) + cp->fontopt = BOLD; + } + cp->fontfam = p; + vrml_font(&cstk[SP]); +} + +static void vrml_set_style(char **s) +{ + char *line; + context_t *cp; + + cp = &(cstk[SP]); + while ((line = *s++)) { + if (streq(line, "solid")) + cp->pen = P_SOLID; + else if (streq(line, "dashed")) + cp->pen = P_DASHED; + else if (streq(line, "dotted")) + cp->pen = P_DOTTED; + else if (streq(line, "bold")) + cp->penwidth = WIDTH_BOLD; + else if (streq(line, "invis")) + cp->pen = P_NONE; + else if (streq(line, "filled")) + cp->fill = P_SOLID; + else if (streq(line, "unfilled")) + cp->fill = P_NONE; + else { + agerr(AGWARN, + "vrml_set_style: unsupported style %s - ignoring\n", + line); + } + } +} + +static void vrml_textpara(GVJ_t *job, point p, textpara_t * para) +{ + obj_state_t *obj = job->obj; + char *fontlist, *err; + pointf mp; + int brect[8]; + extern gdFontPtr gdFontSmall; + + if (! obj->n) + return; + cstk[SP].pencolor_ix = vrml_resolve_color(cstk[SP].pencolor); + fontlist = (char*)(para->layout); /* FIXME - kluge */ + + switch (para->just) { + case 'l': + break; + case 'r': + p.x -= para->width; + break; + default: + case 'n': + p.x -= para->width / 2; + break; + } +/* p.y += cstk[SP].fontsz*2/3; */ + + mp = vrml_node_point(obj->n, p); + + err = gdImageStringFT(im, brect, cstk[SP].pencolor_ix, fontlist, + cstk[SP].fontsz, (Rot ? 90.0 : 0.0) * PI / 180.0, + ROUND(mp.x), ROUND(mp.y), para->str); + if (err) { + /* revert to builtin fonts */ + gdImageString(im, gdFontSmall, ROUND(mp.x), ROUND(mp.y), + (unsigned char *) para->str, cstk[SP].pencolor_ix); + } +} + +/* interpolate_zcoord: + * Given 2 points in 3D p = (fst.x,fst.y,fstz) and q = (snd.x, snd.y, sndz), + * and a point p1 in the xy plane lying on the line segment connecting + * the projections of the p and q, find the z coordinate of p1 when it + * is projected up onto the segment (p,q) in 3-space. + * + * Why the special case for ranks? Is the arithmetic really correct? + */ +static double +interpolate_zcoord(GVJ_t *job, pointf p1, point fst, double fstz, point snd, double sndz) +{ + obj_state_t *obj = job->obj; + edge_t *e = obj->e; + double len, d, rv; + + if (fstz == sndz) + return fstz; + if (ND_rank(e->tail) != ND_rank(e->head)) { + if (snd.y == fst.y) + rv = (fstz + sndz) / 2.0; + else + rv = fstz + (sndz - fstz) * (p1.y - fst.y) / (snd.y - fst.y); + } + else { + len = DIST(fst, snd); + d = DIST(p1, fst)/len; + rv = fstz + d*(sndz - fstz); + } + return rv; +} + +/* collinear: + * Return true if the 3 points starting at A are collinear. + */ +static int +collinear (point * A) +{ + Ppoint_t a, b, c; + double w; + + a.x = A->x; + a.y = A->y; + A++; + b.x = A->x; + b.y = A->y; + A++; + c.x = A->x; + c.y = A->y; + + w = wind(a,b,c); + return (fabs(w) <= 1); +} + +/* straight: + * Return true if bezier points are collinear + * At present, just check with 4 points, the common case. + */ +static int +straight (point * A, int n) +{ + if (n != 4) return 0; + return (collinear(A) && collinear(A+1)); +} + +static void +doSegment (FILE *out, point* A, point p0, double z0, point p1, double z1) +{ + double d1, d0; + double delx, dely, delz; + + delx = p0.x - p1.x; + dely = p0.y - p1.y; + delz = z0 - z1; + EdgeLen = sqrt(delx*delx + dely*dely + delz*delz); + d0 = DIST(A[0],p0); + d1 = DIST(A[3],p1); + CylHt = EdgeLen - d0 - d1; + TailHt = HeadHt = 0; + + IsSegment = 1; + fprintf(out, "Transform {\n"); + fprintf(out, " children [\n"); + fprintf(out, " Shape {\n"); + fprintf(out, " geometry Cylinder {\n"); + fprintf(out, " bottom FALSE top FALSE\n"); + fprintf(out, " height %f radius %d }\n", CylHt, cstk[SP].penwidth); + fprintf(out, " appearance Appearance {\n"); + fprintf(out, " material Material {\n"); + fprintf(out, " ambientIntensity 0.33\n"); + fprintf(out, " diffuseColor %f %f %f\n", + cstk[SP].r,cstk[SP].g,cstk[SP].b); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, " }\n"); +} + +static void +vrml_bezier(GVJ_t *job, point * A, int n, int arrow_at_start, int arrow_at_end, int filled) +{ + FILE *out = job->output_file; + obj_state_t *obj = job->obj; + edge_t *e = obj->e; + pointf p1, V[4]; + int i, j, step; + double fstz, sndz; + context_t *cp; + + assert(obj->e); + + cp = &(cstk[SP]); + if (cp->pen == P_NONE) + return; + fstz = Fstz = late_double(e->tail, N_z, 0.0, -1000.0); + sndz = Sndz = late_double(e->head, N_z, 0.0, -MAXFLOAT); + if (straight(A,n)) { + doSegment (out, A, ND_coord_i(e->tail),Fstz,ND_coord_i(e->head),Sndz); + return; + } + + fprintf(out, "Shape { geometry Extrusion {\n"); + fprintf(out, " spine ["); + V[3].x = A[0].x; + V[3].y = A[0].y; + for (i = 0; i + 3 < n; i += 3) { + V[0] = V[3]; + for (j = 1; j <= 3; j++) { + V[j].x = A[i + j].x; + V[j].y = A[i + j].y; + } + for (step = 0; step <= BEZIERSUBDIVISION; step++) { + p1 = Bezier(V, 3, (double) step / BEZIERSUBDIVISION, NULL, + NULL); + fprintf(out, " %.3f %.3f %.3f", p1.x, p1.y, + interpolate_zcoord(job, p1, A[0], fstz, A[n - 1], sndz)); + } + } + fprintf(out, " ]\n"); + fprintf(out, " crossSection [ %d %d, %d %d, %d %d, %d %d ]\n", + (cp->penwidth), (cp->penwidth), -(cp->penwidth), + (cp->penwidth), -(cp->penwidth), -(cp->penwidth), + (cp->penwidth), -(cp->penwidth)); + fprintf(out, "}\n"); + fprintf(out, " appearance DEF E%d Appearance {\n", e->id); + fprintf(out, " material Material {\n"); + fprintf(out, " ambientIntensity 0.33\n"); + fprintf(out, " diffuseColor %.3f %.3f %.3f\n", + cstk[SP].r, cstk[SP].g, cstk[SP].b); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, "}\n"); +} + +/* doArrowhead: + * If edge is straight, we attach a cone to the edge as a group. + */ +static void +doArrowhead (GVJ_t *job, point* A) +{ + FILE *out = job->output_file; + obj_state_t *obj = job->obj; + edge_t *e = obj->e; + double rad, ht, y; + pointf p0; /* center of triangle base */ + point tp,hp; + + p0.x = (A[0].x + A[2].x)/2.0; + p0.y = (A[0].y + A[2].y)/2.0; + rad = DIST(A[0],A[2])/2.0; + ht = DIST(p0,A[1]); + + y = (CylHt + ht)/2.0; + + tp = ND_coord_i(e->tail); + hp = ND_coord_i(e->head); + fprintf(out, "Transform {\n"); + if (DIST2(A[1], tp) < DIST2(A[1], hp)) { + TailHt = ht; + fprintf(out, " translation 0 -%.3f 0\n", y); + fprintf(out, " rotation 0 0 1 %.3f\n", PI); + } + else { + HeadHt = ht; + fprintf(out, " translation 0 %.3f 0\n", y); + } + fprintf(out, " children [\n"); + fprintf(out, " Shape {\n"); + fprintf(out, " geometry Cone {bottomRadius %.3f height %.3f }\n", + rad, ht); + fprintf(out, " appearance Appearance {\n"); + fprintf(out, " material Material {\n"); + fprintf(out, " ambientIntensity 0.33\n"); + fprintf(out, " diffuseColor %f %f %f\n", cstk[SP].r,cstk[SP].g,cstk[SP].b); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, "}\n"); +} + +static void vrml_polygon(GVJ_t *job, point * A, int n, int filled) +{ + FILE *out = job->output_file; + obj_state_t *obj = job->obj; + graph_t *g = obj->g; + node_t *n = obj->n; + edge_t *e = obj->e; + pointf p, mp; + int i; + gdPoint *points; + int style[20]; + int pen, width; + gdImagePtr brush = NULL; + double theta, z; + node_t *endp; + + if (g) { + fprintf(out, " Background { skyColor %.3f %.3f %.3f }\n", + cstk[SP].r, cstk[SP].g, cstk[SP].b); + Saw_skycolor = TRUE; + } + else if (n) { + + if (cstk[SP].pen != P_NONE) { + cstk[SP].pencolor_ix = vrml_resolve_color(cstk[SP].pencolor); + cstk[SP].fillcolor_ix = vrml_resolve_color(cstk[SP].fillcolor); + if (cstk[SP].pen == P_DASHED) { + for (i = 0; i < 10; i++) + style[i] = cstk[SP].pencolor_ix; + for (; i < 20; i++) + style[i] = gdTransparent; + gdImageSetStyle(im, style, 20); + pen = gdStyled; + } else if (cstk[SP].pen == P_DOTTED) { + for (i = 0; i < 2; i++) + style[i] = cstk[SP].pencolor_ix; + for (; i < 12; i++) + style[i] = gdTransparent; + gdImageSetStyle(im, style, 12); + pen = gdStyled; + } else { + pen = cstk[SP].pencolor_ix; + } + if (cstk[SP].penwidth != WIDTH_NORMAL) { + width = cstk[SP].penwidth; + brush = gdImageCreate(width, width); + gdImagePaletteCopy(brush, im); + gdImageFilledRectangle(brush, + 0, 0, width - 1, width - 1, + cstk[SP].pencolor_ix); + gdImageSetBrush(im, brush); + if (pen == gdStyled) + pen = gdStyledBrushed; + else + pen = gdBrushed; + } + points = N_GNEW(n, gdPoint); + for (i = 0; i < n; i++) { + mp = vrml_node_point(A[i]); + points[i].x = ROUND(mp.x); + points[i].y = ROUND(mp.y); + } + if (filled) + gdImageFilledPolygon(im, points, n, cstk[SP].fillcolor_ix); + gdImagePolygon(im, points, n, pen); + free(points); + if (brush) + gdImageDestroy(brush); + } + + z = late_double(n, N_z, 0.0, -MAXFLOAT); + + fprintf(out, "Shape {\n"); + fprintf(out, " appearance Appearance {\n"); + fprintf(out, " material Material {\n"); + fprintf(out, " ambientIntensity 0.33\n"); + fprintf(out, " diffuseColor 1 1 1\n"); + fprintf(out, " }\n"); + fprintf(out, " texture ImageTexture { url \"node%d.png\" }\n", + n->id); + fprintf(out, " }\n"); + fprintf(out, " geometry Extrusion {\n"); + fprintf(out, " crossSection ["); + for (i = 0; i < n; i++) { + p.x = A[i].x - ND_coord_i(n).x; + p.y = A[i].y - ND_coord_i(n).y; + fprintf(out, " %.3f %.3f,", p.x, p.y); + } + p.x = A[0].x - ND_coord_i(n).x; + p.y = A[0].y - ND_coord_i(n).y; + fprintf(out, " %.3f %.3f ]\n", p.x, p.y); + fprintf(out, " spine [ %d %d %.3f, %d %d %.3f ]\n", + ND_coord_i(n).x, ND_coord_i(n).y, z - .01, + ND_coord_i(n).x, ND_coord_i(n).y, z + .01); + fprintf(out, " }\n"); + fprintf(out, "}\n"); + + } + else if (e) { + if (cstk[SP].pen == P_NONE) + return; + if (n != 3) { + static int flag; + if (!flag) { + flag++; + agerr(AGWARN, + "vrml_polygon: non-triangle arrowheads not supported - ignoring\n"); + } + } + if (IsSegment) { + doArrowhead (job, A); + return; + } + p.x = p.y = 0.0; + for (i = 0; i < n; i++) { + p.x += A[i].x; + p.y += A[i].y; + } + p.x = p.x / n; + p.y = p.y / n; + + /* it is bad to know that A[1] is the aiming point, but we do */ + theta = + atan2((A[0].y + A[2].y) / 2.0 - A[1].y, + (A[0].x + A[2].x) / 2.0 - A[1].x) + PI / 2.0; + + + /* this is gruesome, but how else can we get z coord */ + if (DIST2(p, ND_coord_i(e->tail)) < + DIST2(p, ND_coord_i(e->head))) + endp = e->tail; + else + endp = e->head; + z = late_double(endp, N_z, 0.0, -MAXFLOAT); + + /* FIXME: arrow vector ought to follow z coord of bezier */ + fprintf(out, "Transform {\n"); + fprintf(out, " translation %.3f %.3f %.3f\n", p.x, p.y, z); + fprintf(out, " children [\n"); + fprintf(out, " Transform {\n"); + fprintf(out, " rotation 0 0 1 %.3f\n", theta); + fprintf(out, " children [\n"); + fprintf(out, " Shape {\n"); + fprintf(out, + " geometry Cone {bottomRadius %.3f height %.3f }\n", + cstk[SP].penwidth * 2.5, cstk[SP].penwidth * 10.0); + fprintf(out, " appearance USE E%d\n", e->id); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, "}\n"); + } +} + +/* doSphere: + * Output sphere in VRML for point nodes. + */ +static void +doSphere (FILE *out, node_t *n, point p, int rx, int ry) +{ + pointf mp; + double z; + + if (!(strcmp(cstk[SP].fillcolor, "transparent"))) { + return; + } + + mp.x = ND_coord_i(n).x; + mp.y = ND_coord_i(n).y; + + z = late_double(n, N_z, 0.0, -MAXFLOAT); + + fprintf(out, "Transform {\n"); + fprintf(out, " translation %.3f %.3f %.3f\n", mp.x, mp.y, z); + fprintf(out, " scale %d %d %d\n", rx, rx, rx); + fprintf(out, " children [\n"); + fprintf(out, " Transform {\n"); + fprintf(out, " children [\n"); + fprintf(out, " Shape {\n"); + fprintf(out, " geometry Sphere { radius 1.0 }\n"); + fprintf(out, " appearance Appearance {\n"); + fprintf(out, " material Material {\n"); + fprintf(out, " ambientIntensity 0.33\n"); + fprintf(out, " diffuseColor %f %f %f\n", + cstk[SP].r,cstk[SP].g,cstk[SP].b); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, "}\n"); +} + +static void vrml_ellipse(GVJ_t *job, point p, int rx, int ry, int filled) +{ + FILE *out = job->output_file; + obj_state_t *obj = job->obj; + graph_t *g = obj->g; + node_t *n = obj->n; + edge_t *e = obj->e; + pointf mp; + int i; + node_t *endp; + int style[40]; /* need 2* size for arcs, I don't know why */ + int pen, width; + gdImagePtr brush = NULL; + double z; + + if (n) { + if (shapeOf(n) == SH_POINT) { + doSphere (out, n, p, rx, ry); + return; + } + cstk[SP].pencolor_ix = vrml_resolve_color(cstk[SP].pencolor); + cstk[SP].fillcolor_ix = vrml_resolve_color(cstk[SP].fillcolor); + if (cstk[SP].pen != P_NONE) { + if (cstk[SP].pen == P_DASHED) { + for (i = 0; i < 20; i++) + style[i] = cstk[SP].pencolor_ix; + for (; i < 40; i++) + style[i] = gdTransparent; + gdImageSetStyle(im, style, 40); + pen = gdStyled; + } else if (cstk[SP].pen == P_DOTTED) { + for (i = 0; i < 2; i++) + style[i] = cstk[SP].pencolor_ix; + for (; i < 24; i++) + style[i] = gdTransparent; + gdImageSetStyle(im, style, 24); + pen = gdStyled; + } else { + pen = cstk[SP].pencolor_ix; + } + if (cstk[SP].penwidth != WIDTH_NORMAL) { + width = cstk[SP].penwidth; + brush = gdImageCreate(width, width); + gdImagePaletteCopy(brush, im); + gdImageFilledRectangle(brush, + 0, 0, width - 1, width - 1, + cstk[SP].pencolor_ix); + gdImageSetBrush(im, brush); + if (pen == gdStyled) + pen = gdStyledBrushed; + else + pen = gdBrushed; + } + mp = vrml_node_point(p); + + if (filled) { + gdImageFilledEllipse(im, ROUND(mp.x), ROUND(mp.y), + ROUND(Scale * (rx + rx)), + ROUND(Scale * (ry + ry)), + cstk[SP].fillcolor_ix); + } + gdImageArc(im, ROUND(mp.x), ROUND(mp.y), + ROUND(Scale * (rx + rx)), ROUND(Scale * (ry + ry)), + 0, 360, pen); + if (brush) + gdImageDestroy(brush); + } + + mp.x = ND_coord_i(n).x; + mp.y = ND_coord_i(n).y; + + z = late_double(n, N_z, 0.0, -MAXFLOAT); + + fprintf(out, "Transform {\n"); + fprintf(out, " translation %.3f %.3f %.3f\n", mp.x, mp.y, z); + fprintf(out, " scale %d %d 1\n", rx, ry); + fprintf(out, " children [\n"); + fprintf(out, " Transform {\n"); + fprintf(out, " rotation 1 0 0 1.57\n"); + fprintf(out, " children [\n"); + fprintf(out, " Shape {\n"); + fprintf(out, " geometry Cylinder { side FALSE }\n"); + fprintf(out, " appearance Appearance {\n"); + fprintf(out, " material Material {\n"); + fprintf(out, " ambientIntensity 0.33\n"); + fprintf(out, " diffuseColor 1 1 1\n"); + fprintf(out, " }\n"); + fprintf(out, " texture ImageTexture { url \"node%d.png\" }\n", n->id); + fprintf(out, " }\n"); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, "}\n"); + + } + else if (e) { + if (cstk[SP].pen == P_NONE) + return; + mp.x = (double) p.x; + mp.y = (double) p.y; + /* this is gruesome, but how else can we get z coord */ + if (DIST2(mp, ND_coord_i(e->tail)) < + DIST2(mp, ND_coord_i(e->head))) + endp = e->tail; + else + endp = e->head; + z = late_double(endp, N_z, 0.0, -MAXFLOAT); + + fprintf(out, "Transform {\n"); + fprintf(out, " translation %.3f %.3f %.3f\n", mp.x, mp.y, z); + fprintf(out, " children [\n"); + fprintf(out, " Shape {\n"); + fprintf(out, " geometry Sphere {radius %.3f }\n", (double) rx); + fprintf(out, " appearance USE E%d\n", e->id); + fprintf(out, " }\n"); + fprintf(out, " ]\n"); + fprintf(out, "}\n"); + } +} + +static void vrml_polyline(point * A, int n) +{ +/* + pointf p, p1; + int i; + + if (cstk[SP].pen != P_NONE) { + p.x = A[0].x; + p.y = A[0].y; + for (i = 1; i < n; i++) { + p1.x = A[i].x; + p1.y = A[i].y; +#ifdef NONEOFTHISEITHER + if (cstk[SP].pen == P_DASHED) { + gdImageDashedLine(im, ROUND(p.x), ROUND(p.y), + ROUND(p1.x), ROUND(p1.y), cstk[SP].color_ix); + } else { + gdImageLine(im, ROUND(p.x), ROUND(p.y), + ROUND(p1.x), ROUND(p1.y), cstk[SP].color_ix); + } +#endif + p.x = p1.x; + p.y = p1.y; + } + } +*/ +} + +static void vrml_usershape(usershape_t *us, boxf b, point *A, int n, bool filled) +{ +/* FIXME */ + vrml_polygon(A, n, filled); +} + +codegen_t VRML_CodeGen = { + 0, /* vrml_reset */ + vrml_begin_job, 0, /* vrml_end_job */ + vrml_begin_graph, vrml_end_graph, + vrml_begin_page, 0, /* vrml_end_page */ + 0, /* vrml_begin_layer */ 0, /* vrml_end_layer */ + 0, /* vrml_begin_cluster */ 0, /* vrml_end_cluster */ + 0, /* vrml_begin_nodes */ 0, /* vrml_end_nodes */ + 0, /* vrml_begin_edges */ 0, /* vrml_end_edges */ + vrml_begin_node, vrml_end_node, + vrml_begin_edge, vrml_end_edge, + vrml_begin_context, vrml_end_context, + 0, /* vrml_begin_anchor */ 0, /* vrml_end_anchor */ + vrml_set_font, vrml_textpara, + vrml_set_pencolor, vrml_set_fillcolor, vrml_set_style, + vrml_ellipse, vrml_polygon, + vrml_bezier, vrml_polyline, + 0, /* bezier_has_arrows */ + 0, /* comment */ + vrml_usershape +}; +#endif /* HAVE_GD_PNG */