1 /* $Id:  */
2 /* vim:set shiftwidth=8 ts=8: */
3 
4 /**********************************************************
5 *            Copyright (c) 2011 Andy Jeutter              *
6 *            AKA HallerHarry at gmx.de                    *
7 *            All rights reserved.                         *
8 **********************************************************/
9 
10 /*************************************************************************
11  * This program and the accompanying materials
12  * are made available under the terms of the Eclipse Public License v1.0
13  * which accompanies this distribution, and is available at
14  * http://www.eclipse.org/legal/epl-v10.html
15  *
16  * Contributors: See CVS logs. Details at http://www.graphviz.org/
17  *************************************************************************/
18 
19 #define _GNU_SOURCE
20 #include "config.h"
21 
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 
28 #include "macros.h"
29 #include "const.h"
30 
31 #include "gvplugin_render.h"
32 #include "gvplugin_device.h"
33 #include "gvio.h"
34 #include "gvcint.h"
35 
36 #define POV_VERSION \
37     "#version 3.6;\n"
38 
39 #define POV_GLOBALS \
40     "global_settings { assumed_gamma 1.0 }\n"
41 
42 #define POV_DEFAULT \
43     "#default { finish { ambient 0.1 diffuse 0.9 } }\n"
44 
45 #define POV_INCLUDE \
46     "#include \"colors.inc\"\n"\
47     "#include \"textures.inc\"\n"\
48     "#include \"shapes.inc\"\n"
49 
50 #define POV_LIGHT \
51     "light_source { <1500,3000,-2500> color White }\n"
52 
53 #define POV_CAMERA \
54     "camera { location <%.3f , %.3f , %.3f>\n"\
55     "         look_at  <%.3f , %.3f , %.3f>\n"\
56     "         right x * image_width / image_height\n"\
57     "         angle %.3f\n"\
58     "}\n"
59 
60 #define POV_SKY_AND_GND \
61     "//sky\n"\
62     "plane { <0, 1, 0>, 1 hollow\n"\
63     "    texture {\n"\
64     "        pigment { bozo turbulence 0.95\n"\
65     "            color_map {\n"\
66     "                [0.00 rgb <0.05, 0.20, 0.50>]\n"\
67     "                [0.50 rgb <0.05, 0.20, 0.50>]\n"\
68     "                [0.75 rgb <1.00, 1.00, 1.00>]\n"\
69     "                [0.75 rgb <0.25, 0.25, 0.25>]\n"\
70     "                [1.00 rgb <0.50, 0.50, 0.50>]\n"\
71     "            }\n"\
72     "            scale <1.00, 1.00, 1.50> * 2.50\n"\
73     "            translate <0.00, 0.00, 0.00>\n"\
74     "        }\n"\
75     "        finish { ambient 1 diffuse 0 }\n"\
76     "    }\n"\
77     "    scale 10000\n"\
78     "}\n"\
79     "//mist\n"\
80     "fog { fog_type 2\n"\
81     "    distance 50\n"\
82     "    color rgb <1.00, 1.00, 1.00> * 0.75\n"\
83     "    fog_offset 0.10\n"\
84     "    fog_alt 1.50\n"\
85     "    turbulence 1.75\n"\
86     "}\n"\
87     "//gnd\n"\
88     "plane { <0.00, 1.00, 0.00>, 0\n"\
89     "    texture {\n"\
90     "        pigment{ color rgb <0.25, 0.45, 0.00> }\n"\
91     "        normal { bumps 0.75 scale 0.01 }\n"\
92     "        finish { phong 0.10 }\n"\
93     "    }\n"\
94     "}\n"
95 
96 #define POV_BOX \
97     "box { <%.3f, %.3f, %.3f>, <%.3f, %.3f, %.3f>\n"
98 
99 #define POV_SCALE1 \
100     "scale %.3f\n"
101 
102 #define POV_SCALE3 \
103     "scale    "POV_VECTOR3"\n"
104 
105 #define POV_ROTATE \
106     "rotate   "POV_VECTOR3"\n"
107 
108 #define POV_TRANSLATE \
109     "translate"POV_VECTOR3"\n"
110 
111 #define END \
112     "}\n"
113 
114 #define POV_TORUS \
115     "torus { %.3f, %.3f\n"
116 
117 #define POV_SPHERE_SWEEP \
118     "sphere_sweep {\n"\
119     "    %s\n"\
120     "    %d,\n"
121 
122 #define POV_SPHERE \
123     "sphere {"POV_VECTOR3", 1.0\n"	// center, radius
124 
125 #define POV_TEXT \
126     "text {\n"\
127     "    ttf \"%s\",\n"\
128     "    \"%s\", %.3f, %.3f\n"
129 
130 #define POV_DECLARE \
131     "#declare %s = %s;\n"
132 
133 #define POV_OBJECT \
134     "object { %s }\n"
135 
136 #define POV_VERBATIM \
137     "%s\n"
138 
139 #define POV_DEBUG \
140     "#debug %s\n"
141 
142 #define POV_POLYGON \
143     "polygon { %d,\n"
144 
145 #define POV_VECTOR3 \
146     "<%9.3f, %9.3f, %9.3f>"
147 
148 #define POV_PIGMENT_COLOR \
149     "pigment { color %s }\n"
150 
151 #define POV_COLOR_NAME \
152     "%s transmit %.3f"
153 
154 #define POV_COLOR_RGB \
155     "rgb"POV_VECTOR3" transmit %.3f"
156 
157 //colors are taken from /usr/share/povray-3.6/include/colors.inc
158 //list must be LANG_C sorted (all lower case)
159 #define POV_COLORS \
160 "aquamarine",\
161 "bakerschoc",\
162 "black",\
163 "blue",\
164 "blueviolet",\
165 "brass",\
166 "brightgold",\
167 "bronze",\
168 "bronze2",\
169 "brown",\
170 "cadetblue",\
171 "clear",\
172 "coolcopper",\
173 "copper",\
174 "coral",\
175 "cornflowerblue",\
176 "cyan",\
177 "darkbrown",\
178 "darkgreen",\
179 "darkolivegreen",\
180 "darkorchid",\
181 "darkpurple",\
182 "darkslateblue",\
183 "darkslategray",\
184 "darkslategrey",\
185 "darktan",\
186 "darkturquoise",\
187 "darkwood",\
188 "dkgreencopper",\
189 "dustyrose",\
190 "feldspar",\
191 "firebrick",\
192 "flesh",\
193 "forestgreen",\
194 "gold",\
195 "goldenrod",\
196 "gray05",\
197 "gray10",\
198 "gray15",\
199 "gray20",\
200 "gray25",\
201 "gray30",\
202 "gray35",\
203 "gray40",\
204 "gray45",\
205 "gray50",\
206 "gray55",\
207 "gray60",\
208 "gray65",\
209 "gray70",\
210 "gray75",\
211 "gray80",\
212 "gray85",\
213 "gray90",\
214 "gray95",\
215 "green",\
216 "greencopper",\
217 "greenyellow",\
218 "huntersgreen",\
219 "indianred",\
220 "khaki",\
221 "lightblue",\
222 "light_purple",\
223 "lightsteelblue",\
224 "lightwood",\
225 "limegreen",\
226 "magenta",\
227 "mandarinorange",\
228 "maroon",\
229 "mediumaquamarine",\
230 "mediumblue",\
231 "mediumforestgreen",\
232 "mediumgoldenrod",\
233 "mediumorchid",\
234 "mediumseagreen",\
235 "mediumslateblue",\
236 "mediumspringgreen",\
237 "mediumturquoise",\
238 "mediumvioletred",\
239 "mediumwood",\
240 "med_purple",\
241 "mica",\
242 "midnightblue",\
243 "navy",\
244 "navyblue",\
245 "neonblue",\
246 "neonpink",\
247 "newmidnightblue",\
248 "newtan",\
249 "oldgold",\
250 "orange",\
251 "orangered",\
252 "orchid",\
253 "palegreen",\
254 "pink",\
255 "plum",\
256 "quartz",\
257 "red",\
258 "richblue",\
259 "salmon",\
260 "scarlet",\
261 "seagreen",\
262 "semiSweetChoc",\
263 "sienna",\
264 "silver",\
265 "skyblue",\
266 "slateblue",\
267 "spicypink",\
268 "springgreen",\
269 "steelblue",\
270 "summersky",\
271 "tan",\
272 "thistle",\
273 "turquoise",\
274 "verydarkbrown",\
275 "very_light_purple",\
276 "violet",\
277 "violetred",\
278 "wheat",\
279 "white",\
280 "yellow",\
281 "yellowgreen"
282 
283 #define GV_OBJ_EXT(type, obj, name) \
284 	do { \
285 		char debug_str[256]; \
286 		gvprintf(job, POV_DECLARE, type, obj); \
287 		gvprintf(job, POV_OBJECT, type); \
288 		gvprintf(job, POV_DECLARE, "Min", "min_extent("type")"); \
289 		gvprintf(job, POV_DECLARE, "Max", "max_extent("type")"); \
290 		snprintf(debug_str, 256,  \
291 			"concat(\"Dim = \" , vstr(3, Max - Min, \", \", 0, 3)," \
292 			" \" "type": %s\", \"\\n\")", name); \
293 		gvprintf(job, POV_DEBUG, debug_str); \
294 	} while (0)
295 
296 /*
297 //png, gif, NO jpg!
298 pigment
299 { image_map
300   { gif "image.gif"
301     map_type 1
302   }
303 }
304 */
305 
306 /*
307 #declare Sphere =
308 sphere {
309   <0,0,0>, 1
310   pigment { rgb <1,0,0> }
311 }
312 #declare Min = min_extent ( Sphere );
313 #declare Max = max_extent ( Sphere );
314 object { Sphere }
315 box {
316     Min, Max
317     pigment { rgbf <1,1,1,0.5> }
318     scale<20,20,20>
319 }
320 */
321 
322 /*
323 STRING functions
324 
325 str( float , min_len , digits_after_dot )
326 concat( STRING , STRING , [STRING ,...])
327 chr( INT )
328 substr( STRING , INT , INT )
329 strupr( STRING )
330 strlwr( STRING )
331 vstr( vec_dimension , vec, sep_str, min_len, digits_after_dot )
332 
333 examples:
334 #debug vstr(3, Min, ", ", 0, 3)
335 #debug "\n*****************\n"
336 #debug concat ( "Max =", vstr(3, Max, ", ", 0, 3), chr(13), chr(10) )
337 */
338 
339 
340 #define DPI 72.0
341 #define RENDERER_COLOR_TYPE RGBA_BYTE
342 typedef enum { FORMAT_POV, } format_type;
343 
344 //#define DEBUG
345 
346 //TODO: check why this dot file does not work (90 rotated)
347 //   /usr/share/graphviz/graphs/directed/NaN.gv
348 //TODO: add Texttures
349 //TODO: check how we can receive attributes from dot file
350 //   if we can't receive attributes set defaults in pov include file
351 //   - put #include "graph-scheme-fancy.inc" in pov file
352 //   - run povray with +L`pwd`
353 //   - put e.g. #declare mycolor = Gold; in graph-scheme-fancy.inc
354 //   - use textures and color: pigment { color mycolor transmit 0.000 }
355 //TODO: idea, put the whole graph in a declare= and then
356 //   print it with something along the line:
357 //   object{ graph translate{page->translation, ...} rotate{page->rotation, ...} }
358 
359 static char *pov_knowncolors[] = { POV_COLORS };
360 
361 static float layerz = 0;
362 static float z = 0;
363 
el(GVJ_t * job,char * template,...)364 char *el(GVJ_t* job, char *template, ...)
365 {
366 #if defined(HAVE_VASPRINTF)
367 	char *str;
368 	va_list arglist;
369 
370 	va_start(arglist, template);
371 	vasprintf(&str, template, arglist);
372 	va_end(arglist);
373 
374 	return str;
375 #elif defined(HAVE_VSNPRINTF)
376 	char buf[BUFSIZ];
377 	int len;
378 	char *str;
379 	va_list arglist;
380 
381 	va_start(arglist, template);
382 	len = vsnprintf((char *)buf, BUFSIZ, template, arglist);
383 	if (len < 0) {
384 		job->common->errorfn("pov renderer:el - %s\n", strerror(errno));
385 		str = strdup ("");
386 	}
387 	else if (len >= BUFSIZ) {
388 		str = malloc (len+1);
389 		va_end(arglist);
390 		va_start(arglist, template);
391 		len = vsprintf(str, template, arglist);
392 	}
393 	else {
394 		str = strdup (buf);
395 	}
396 	va_end(arglist);
397 
398 	return str;
399 #else
400 /* Dummy function that will never be used */
401 	return strdup("");
402 #endif
403 }
404 
pov_color_as_str(GVJ_t * job,gvcolor_t color,float transparency)405 static char *pov_color_as_str(GVJ_t * job, gvcolor_t color, float transparency)
406 {
407 	char *pov, *c = NULL;
408 	switch (color.type) {
409 	case COLOR_STRING:
410 #ifdef DEBUG
411 		gvprintf(job, "// type = %d, color = %s\n", color.type, color.u.string);
412 #endif
413 		if (!strcmp(color.u.string, "red"))
414 			c = el(job, POV_COLOR_NAME, "Red", transparency);
415 		else if (!strcmp(color.u.string, "green"))
416 			c = el(job, POV_COLOR_NAME, "Green", transparency);
417 		else if (!strcmp(color.u.string, "blue"))
418 			c = el(job, POV_COLOR_NAME, "Blue", transparency);
419 		else
420 			c = el(job, POV_COLOR_NAME, color.u.string, transparency);
421 		break;
422 	case RENDERER_COLOR_TYPE:
423 #ifdef DEBUG
424 		gvprintf(job, "// type = %d, color = %d, %d, %d\n",
425 			 color.type, color.u.rgba[0], color.u.rgba[1],
426 			 color.u.rgba[2]);
427 #endif
428 		c = el(job, POV_COLOR_RGB,
429 		       color.u.rgba[0] / 256.0, color.u.rgba[1] / 256.0,
430 		       color.u.rgba[2] / 256.0, transparency);
431 		break;
432 	default:
433 		fprintf(stderr,
434 			"oops, internal error: unhandled color type=%d %s\n",
435 			color.type, color.u.string);
436 		assert(0);	//oops, wrong type set in gvrender_features_t?
437 	}
438 	pov = el(job, POV_PIGMENT_COLOR, c);
439 	free(c);
440 	return pov;
441 }
442 
pov_comment(GVJ_t * job,char * str)443 static void pov_comment(GVJ_t * job, char *str)
444 {
445 	gvprintf(job, "//*** comment: %s\n", str);
446 }
447 
pov_begin_job(GVJ_t * job)448 static void pov_begin_job(GVJ_t * job)
449 {
450 	gvputs(job, POV_VERSION);
451 	gvputs(job, POV_GLOBALS);
452 	gvputs(job, POV_DEFAULT);
453 	gvputs(job, POV_INCLUDE);
454 	gvprintf(job, POV_DECLARE, "black", "Black");
455 	gvprintf(job, POV_DECLARE, "white", "White");
456 }
457 
pov_begin_graph(GVJ_t * job)458 static void pov_begin_graph(GVJ_t * job)
459 {
460 	float x, y, d, px, py;
461 
462 	gvprintf(job, "//*** begin_graph %s\n", agnameof(job->obj->u.g));
463 #ifdef DEBUG
464 	gvprintf(job, "// graph_index = %d, pages = %d, layer = %d/%d\n",
465 		 job->graph_index, job->numPages, job->layerNum,
466 		 job->numLayers);
467 	gvprintf(job, "// pagesArraySize.x,y = %d,%d\n", job->pagesArraySize.x,
468 		 job->pagesArraySize.y);
469 	gvprintf(job, "// pagesArrayFirst.x,y = %d,%d\n",
470 		 job->pagesArrayFirst.x, job->pagesArrayFirst.y);
471 	gvprintf(job, "// pagesArrayElem.x,y = %d,%d\n", job->pagesArrayElem.x,
472 		 job->pagesArrayElem.y);
473 	gvprintf(job, "// bb.LL,UR = %.3f,%.3f, %.3f,%.3f\n", job->bb.LL.x,
474 		 job->bb.LL.y, job->bb.UR.x, job->bb.UR.y);
475 	gvprintf(job, "// pageBox in graph LL,UR = %.3f,%.3f, %.3f,%.3f\n",
476 		 job->pageBox.LL.x, job->pageBox.LL.y, job->pageBox.UR.x,
477 		 job->pageBox.UR.y);
478 	gvprintf(job, "// pageSize.x,y = %.3f,%.3f\n", job->pageSize.x,
479 		 job->pageSize.y);
480 	gvprintf(job, "// focus.x,y = %.3f,%.3f\n", job->focus.x, job->focus.y);
481 	gvprintf(job, "// zoom = %.3f, rotation = %d\n", job->zoom,
482 		 (float)job->rotation);
483 	gvprintf(job, "// view port.x,y = %.3f,%.3f\n", job->view.x,
484 		 job->view.y);
485 	gvprintf(job, "// canvasBox LL,UR = %.3f,%.3f, %.3f,%.3f\n",
486 		 job->canvasBox.LL.x, job->canvasBox.LL.y, job->canvasBox.UR.x,
487 		 job->canvasBox.UR.y);
488 	gvprintf(job, "// pageBoundingBox LL,UR = %d,%d, %d,%d\n",
489 		 job->pageBoundingBox.LL.x, job->pageBoundingBox.LL.y,
490 		 job->pageBoundingBox.UR.x, job->pageBoundingBox.UR.y);
491 	gvprintf(job, "// boundingBox (all pages) LL,UR = %d,%d, %d,%d\n",
492 		 job->boundingBox.LL.x, job->boundingBox.LL.y,
493 		 job->boundingBox.UR.x, job->boundingBox.UR.y);
494 	gvprintf(job, "// scale.x,y = %.3f,%.3f\n", job->scale.x, job->scale.y);
495 	gvprintf(job, "// translation.x,y = %.3f,%.3f\n", job->translation.x,
496 		 job->translation.y);
497 	gvprintf(job, "// devscale.x,y = %.3f,%.3f\n", job->devscale.x,
498 		 job->devscale.y);
499 	gvprintf(job, "// verbose = %d\n", job->common->verbose);
500 	gvprintf(job, "// cmd = %s\n", job->common->cmdname);
501 	gvprintf(job, "// info = %s, %s, %s\n", job->common->info[0],
502 		 job->common->info[1], job->common->info[2]);
503 #endif
504 
505 	//setup scene
506 	x = job->view.x / 2.0 * job->scale.x;
507 	y = job->view.y / 2.0 * job->scale.y;
508 	d = -500;
509 	px = atanf(x / fabsf(d)) * 180 / M_PI * 2;
510 	py = atanf(y / fabsf(d)) * 180 / M_PI * 2;
511 	gvprintf(job, POV_CAMERA, x, y, d, x, y, 0.0,
512 		 (px > py ? px : py) * 1.2);
513 	gvputs(job, POV_SKY_AND_GND);
514 	gvputs(job, POV_LIGHT);
515 }
516 
pov_end_graph(GVJ_t * job)517 static void pov_end_graph(GVJ_t * job)
518 {
519 	gvputs(job, "//*** end_graph\n");
520 }
521 
pov_begin_layer(GVJ_t * job,char * layername,int layerNum,int numLayers)522 static void pov_begin_layer(GVJ_t * job, char *layername, int layerNum, int numLayers)
523 {
524 	gvprintf(job, "//*** begin_layer: %s, %d/%d\n", layername, layerNum,
525 		 numLayers);
526 	layerz = layerNum * -10;
527 }
528 
pov_end_layer(GVJ_t * job)529 static void pov_end_layer(GVJ_t * job)
530 {
531 	gvputs(job, "//*** end_layer\n");
532 }
533 
pov_begin_page(GVJ_t * job)534 static void pov_begin_page(GVJ_t * job)
535 {
536 	gvputs(job, "//*** begin_page\n");
537 }
538 
pov_end_page(GVJ_t * job)539 static void pov_end_page(GVJ_t * job)
540 {
541 	gvputs(job, "//*** end_page\n");
542 }
543 
pov_begin_cluster(GVJ_t * job)544 static void pov_begin_cluster(GVJ_t * job)
545 {
546 	gvputs(job, "//*** begin_cluster\n");
547 	layerz -= 2;
548 }
549 
pov_end_cluster(GVJ_t * job)550 static void pov_end_cluster(GVJ_t * job)
551 {
552 	gvputs(job, "//*** end_cluster\n");
553 }
554 
pov_begin_node(GVJ_t * job)555 static void pov_begin_node(GVJ_t * job)
556 {
557 	gvprintf(job, "//*** begin_node: %s\n", agnameof(job->obj->u.n));
558 }
559 
pov_end_node(GVJ_t * job)560 static void pov_end_node(GVJ_t * job)
561 {
562 	gvputs(job, "//*** end_node\n");
563 }
564 
pov_begin_edge(GVJ_t * job)565 static void pov_begin_edge(GVJ_t * job)
566 {
567 	gvputs(job, "//*** begin_edge\n");
568 	layerz -= 5;
569 #ifdef DEBUG
570 	gvprintf(job, "// layerz = %.3f\n", layerz);
571 #endif
572 }
573 
pov_end_edge(GVJ_t * job)574 static void pov_end_edge(GVJ_t * job)
575 {
576 	gvputs(job, "//*** end_edge\n");
577 	layerz += 5;
578 #ifdef DEBUG
579 	gvprintf(job, "// layerz = %.3f\n", layerz);
580 #endif
581 }
582 
pov_textspan(GVJ_t * job,pointf c,textspan_t * span)583 static void pov_textspan(GVJ_t * job, pointf c, textspan_t * span)
584 {
585 	double x, y;
586 	char *pov, *s, *r, *t, *p;
587 
588 	gvprintf(job, "//*** textspan: %s, fontsize = %.3f, fontname = %s\n",
589 		 span->str, span->font->size, span->font->name);
590 	z = layerz - 9;
591 
592 #ifdef DEBUG
593 	if (span->font->postscript_alias)
594 		gvputs(job, "// Warning: postscript_alias not handled!\n");
595 #endif
596 
597 	//handle text justification
598 	switch (span->just) {
599 	case 'l':		//left justified
600 		break;
601 	case 'r':		//right justified
602 		c.x = c.x - span->size.x;
603 		break;
604 	default:
605 	case 'n':		//centered
606 		c.x = c.x - span->size.x / 2.0;
607 		break;
608 	}
609 
610 	x = (c.x + job->translation.x) * job->scale.x;
611 	y = (c.y + job->translation.y) * job->scale.y;
612 
613 	s = el(job, POV_SCALE1, span->font->size * job->scale.x);
614 	r = el(job, POV_ROTATE, 0.0, 0.0, (float)job->rotation);
615 	t = el(job, POV_TRANSLATE, x, y, z);
616 	p = pov_color_as_str(job, job->obj->pencolor, 0.0);
617 
618 	//pov bundled fonts: timrom.ttf, cyrvetic.ttf
619 	pov = el(job, POV_TEXT "    %s    %s    %s    %s    %s" END,
620 		span->font->name, 0.25, 0.0,	//font, depth (0.5 ... 2.0), offset
621 		span->str, "    no_shadow\n", s, r, t, p);
622 
623 #ifdef DEBUG
624 	GV_OBJ_EXT("Text", pov, span->str);
625 	gvprintf(job, "sphere{<0, 0, 0>, 2\ntranslate<%f, %f, %f>\n"
626 		 "pigment{color Red}\nno_shadow\n}\n", x, y, z - 1);
627 #else
628 	gvputs(job, pov);
629 #endif
630 
631 	free(pov);
632 	free(r);
633 	free(p);
634 	free(t);
635 	free(s);
636 }
637 
pov_ellipse(GVJ_t * job,pointf * A,int filled)638 static void pov_ellipse(GVJ_t * job, pointf * A, int filled)
639 {
640 	char *pov, *s, *r, *t, *p;
641 	float cx, cy, rx, ry, w;
642 
643 	gvputs(job, "//*** ellipse\n");
644 	z = layerz - 6;
645 
646 	// A[0] center, A[1] corner of ellipse
647 	cx = (A[0].x + job->translation.x) * job->scale.x;
648 	cy = (A[0].y + job->translation.y) * job->scale.y;
649 	rx = (A[1].x - A[0].x) * job->scale.x;
650 	ry = (A[1].y - A[0].y) * job->scale.y;
651 	w = job->obj->penwidth / (rx + ry) / 2.0 * 5;
652 
653 	//draw rim (torus)
654 	s = el(job, POV_SCALE3, rx, (rx + ry) / 4.0, ry);
655 	r = el(job, POV_ROTATE, 90.0, 0.0, (float)job->rotation);
656 	t = el(job, POV_TRANSLATE, cx, cy, z);
657 	p = pov_color_as_str(job, job->obj->pencolor, 0.0);
658 
659 	pov = el(job, POV_TORUS "    %s    %s    %s    %s" END, 1.0, w,	//radius, size of ring
660 		 s, r, t, p);
661 
662 #ifdef DEBUG
663 	GV_OBJ_EXT("Torus", pov, "");
664 	gvprintf(job, "sphere{<0, 0, 0>, 2\ntranslate<%f, %f, %f>\n"
665 		 "pigment{color Green}\nno_shadow\n}\n", cx, cy, z - 1);
666 #else
667 	gvputs(job, pov);
668 #endif
669 
670 	free(s);
671 	free(r);
672 	free(t);
673 	free(p);
674 	free(pov);
675 
676 	//backgroud of ellipse if filled
677 	if (filled) {
678 		s = el(job, POV_SCALE3, rx, ry, 1.0);
679 		r = el(job, POV_ROTATE, 0.0, 0.0, (float)job->rotation);
680 		t = el(job, POV_TRANSLATE, cx, cy, z);
681 		p = pov_color_as_str(job, job->obj->fillcolor, 0.0);
682 
683 		pov = el(job, POV_SPHERE "    %s    %s    %s    %s" END,
684 			 0.0, 0.0, 0.0, s, r, t, p);
685 
686 		gvputs(job, pov);
687 
688 		free(s);
689 		free(r);
690 		free(t);
691 		free(p);
692 		free(pov);
693 	}
694 }
695 
pov_bezier(GVJ_t * job,pointf * A,int n,int arrow_at_start,int arrow_at_end,int filled)696 static void pov_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
697 		       int arrow_at_end, int filled)
698 {
699 	int i;
700 	char *v, *x;
701 	char *pov, *s, *r, *t, *p;
702 
703 	gvputs(job, "//*** bezier\n");
704 	z = layerz - 4;
705 
706 	s = el(job, POV_SCALE3, job->scale.x, job->scale.y, 1.0);
707 	r = el(job, POV_ROTATE, 0.0, 0.0, (float)job->rotation);
708 	t = el(job, POV_TRANSLATE, 0.0, 0.0, z - 2);
709 	p = pov_color_as_str(job, job->obj->fillcolor, 0.0);
710 
711 	pov = el(job, POV_SPHERE_SWEEP, "b_spline", n + 2);
712 
713 	for (i = 0; i < n; i++) {
714 		v = el(job, POV_VECTOR3 ", %.3f\n", A[i].x + job->translation.x, A[i].y + job->translation.y, 0.0, job->obj->penwidth);	//z coordinate, thickness
715 		x = el(job, "%s    %s", pov, v);	//catenate pov & vector v
716 		free(v);
717 		free(pov);
718 		pov = x;
719 
720 		//TODO: we currently just use the start and end points of the curve as
721 		//control points but we should use center of nodes
722 		if (i == 0 || i == n - 1) {
723 			v = el(job, POV_VECTOR3 ", %.3f\n", A[i].x + job->translation.x, A[i].y + job->translation.y, 0.0, job->obj->penwidth);	//z coordinate, thickness
724 			x = el(job, "%s    %s", pov, v);	//catenate pov & vector v
725 			free(v);
726 			free(pov);
727 			pov = x;
728 		}
729 #ifdef DEBUG
730 		gvprintf(job, "sphere{<0, 0, 0>, 2\ntranslate<%f, %f, %f>\n"
731 			 "pigment{color Yellow}\nno_shadow\n}\n",
732 			 (A[i].x + job->translation.x) * job->scale.x,
733 			 (A[i].y + job->translation.y) * job->scale.y, z - 2);
734 #endif
735 	}
736 	x = el(job, "        tolerance 0.01\n    %s    %s    %s    %s" END, s, r, t,
737 	       p);
738 	pov = el(job, "%s%s", pov, x);	//catenate pov & end str
739 	free(x);
740 
741 	gvputs(job, pov);
742 
743 	free(s);
744 	free(r);
745 	free(t);
746 	free(p);
747 	free(pov);
748 }
749 
pov_polygon(GVJ_t * job,pointf * A,int n,int filled)750 static void pov_polygon(GVJ_t * job, pointf * A, int n, int filled)
751 {
752 	char *pov, *s, *r, *t, *p, *v, *x;
753 	int i;
754 
755 	gvputs(job, "//*** polygon\n");
756 	z = layerz - 2;
757 
758 	s = el(job, POV_SCALE3, job->scale.x, job->scale.y, 1.0);
759 	r = el(job, POV_ROTATE, 0.0, 0.0, (float)job->rotation);
760 	t = el(job, POV_TRANSLATE, 0.0, 0.0, z - 2);
761 	p = pov_color_as_str(job, job->obj->pencolor, 0.0);
762 
763 	pov = el(job, POV_SPHERE_SWEEP, "linear_spline", n + 1);
764 
765 	for (i = 0; i < n; i++) {
766 		v = el(job, POV_VECTOR3 ", %.3f\n", A[i].x + job->translation.x, A[i].y + job->translation.y, 0.0, job->obj->penwidth);	//z coordinate, thickness
767 		x = el(job, "%s    %s", pov, v);	//catenate pov & vector v
768 		free(v);
769 		free(pov);
770 		pov = x;
771 	}
772 
773 	//close polygon, add starting point as final point^
774 	v = el(job, POV_VECTOR3 ", %.3f\n", A[0].x + job->translation.x, A[0].y + job->translation.y, 0.0, job->obj->penwidth);	//z coordinate, thickness
775 
776 	x = el(job, "%s    %s", pov, v);	//catenate pov & vector v
777 	free(v);
778 	free(pov);
779 	pov = x;
780 
781 	x = el(job, "    tolerance 0.1\n    %s    %s    %s    %s" END, s, r, t, p);
782 	pov = el(job, "%s%s", pov, x);	//catenate pov & end str
783 	free(x);
784 
785 	gvputs(job, pov);
786 
787 	free(s);
788 	free(r);
789 	free(t);
790 	free(p);
791 	free(pov);
792 
793 	//create fill background
794 	if (filled) {
795 		s = el(job, POV_SCALE3, job->scale.x, job->scale.y, 1.0);
796 		r = el(job, POV_ROTATE, 0.0, 0.0, (float)job->rotation);
797 		t = el(job, POV_TRANSLATE, 0.0, 0.0, z - 2);
798 		p = pov_color_as_str(job, job->obj->fillcolor, 0.25);
799 
800 		pov = el(job, POV_POLYGON, n);
801 
802 		for (i = 0; i < n; i++) {
803 			//create on z = 0 plane, then translate to real z pos
804 			v = el(job, POV_VECTOR3,
805 			       A[i].x + job->translation.x,
806 			       A[i].y + job->translation.y, 0.0);
807 			x = el(job, "%s\n    %s", pov, v);	//catenate pov & vector v
808 			free(v);
809 			free(pov);
810 			pov = x;
811 		}
812 		x = el(job, "\n    %s    %s    %s    %s" END, s, r, t, p);
813 		pov = el(job, "%s%s", pov, x);	//catenate pov & end str
814 		free(x);
815 
816 		gvputs(job, pov);
817 
818 		free(s);
819 		free(r);
820 		free(t);
821 		free(p);
822 		free(pov);
823 	}
824 }
825 
pov_polyline(GVJ_t * job,pointf * A,int n)826 static void pov_polyline(GVJ_t * job, pointf * A, int n)
827 {
828 	char *pov, *s, *r, *t, *p, *v, *x;
829 	int i;
830 
831 	gvputs(job, "//*** polyline\n");
832 	z = layerz - 6;
833 
834 	s = el(job, POV_SCALE3, job->scale.x, job->scale.y, 1.0);
835 	r = el(job, POV_ROTATE, 0.0, 0.0, (float)job->rotation);
836 	t = el(job, POV_TRANSLATE, 0.0, 0.0, z);
837 	p = pov_color_as_str(job, job->obj->pencolor, 0.0);
838 
839 	pov = el(job, POV_SPHERE_SWEEP, "linear_spline", n);
840 
841 	for (i = 0; i < n; i++) {
842 		v = el(job, POV_VECTOR3 ", %.3f\n", A[i].x + job->translation.x, A[i].y + job->translation.y, 0.0, job->obj->penwidth);	//z coordinate, thickness
843 		x = el(job, "%s    %s", pov, v);	//catenate pov & vector v
844 		free(v);
845 		free(pov);
846 		pov = x;
847 	}
848 
849 	x = el(job, "    tolerance 0.01\n    %s    %s    %s    %s" END, s, r, t, p);
850 	pov = el(job, "%s%s", pov, x);	//catenate pov & end str
851 	free(x);
852 
853 	gvputs(job, pov);
854 
855 	free(s);
856 	free(r);
857 	free(t);
858 	free(p);
859 	free(pov);
860 }
861 
862 gvrender_engine_t pov_engine = {
863 	pov_begin_job,
864 	0,			/* pov_end_job */
865 	pov_begin_graph,
866 	pov_end_graph,
867 	pov_begin_layer,
868 	pov_end_layer,
869 	pov_begin_page,
870 	pov_end_page,
871 	pov_begin_cluster,
872 	pov_end_cluster,
873 	0,			/* pov_begin_nodes */
874 	0,			/* pov_end_nodes */
875 	0,			/* pov_begin_edges */
876 	0,			/* pov_end_edges */
877 	pov_begin_node,
878 	pov_end_node,
879 	pov_begin_edge,
880 	pov_end_edge,
881 	0,			/* pov_begin_anchor */
882 	0,			/* pov_end_anchor */
883 	0,			/* pov_begin_label */
884 	0,			/* pov_end_label */
885 	pov_textspan,
886 	0,			/* pov_resolve_color */
887 	pov_ellipse,
888 	pov_polygon,
889 	pov_bezier,
890 	pov_polyline,
891 	pov_comment,
892 	0,			/* pov_library_shape */
893 };
894 
895 gvrender_features_t render_features_pov = {
896 	/* flags */
897 	GVDEVICE_DOES_LAYERS
898 	    | GVRENDER_DOES_MAP_RECTANGLE
899 	    | GVRENDER_DOES_MAP_CIRCLE
900 	    | GVRENDER_DOES_MAP_POLYGON
901 	    | GVRENDER_DOES_MAP_ELLIPSE
902 	    | GVRENDER_DOES_MAP_BSPLINE
903 	    | GVRENDER_NO_WHITE_BG
904 	    | GVRENDER_DOES_TRANSFORM
905 	    | GVRENDER_DOES_Z | GVRENDER_DOES_MAP_BSPLINE,
906 	4.0,			/* default pad - graph units */
907 	pov_knowncolors,	/* knowncolors */
908 	sizeof(pov_knowncolors) / sizeof(char *),	/* strings in knowncolors */
909 	RENDERER_COLOR_TYPE	/* set renderer color type */
910 };
911 
912 gvdevice_features_t device_features_pov = {
913 	GVDEVICE_DOES_TRUECOLOR,	/* flags */
914 	{0.0, 0.0},		/* default margin - points */
915 	{0.0, 0.0},		/* default page width, height - points */
916 	{DPI, DPI},		/* default dpi */
917 };
918 
919 gvplugin_installed_t gvrender_pov_types[] = {
920 #ifdef HAVE_VSNPRINTF
921 	{FORMAT_POV, "pov", 1, &pov_engine, &render_features_pov},
922 #endif
923 	{0, NULL, 0, NULL, NULL}
924 };
925 
926 gvplugin_installed_t gvdevice_pov_types[] = {
927 #ifdef HAVE_VSNPRINTF
928 	{FORMAT_POV, "pov:pov", 1, NULL, &device_features_pov},
929 #endif
930 	{0, NULL, 0, NULL, NULL}
931 };
932 
933