1 /*
2 * A simple utility for exporting Inkscape svg Shapes as PovRay bezier
3 * prisms. Note that this is output-only, and would thus seem to be
4 * better placed as an 'export' rather than 'output'. However, Export
5 * handles all or partial documents, while this outputs ALL shapes in
6 * the current SVG document.
7 *
8 * For information on the PovRay file format, see:
9 * http://www.povray.org
10 *
11 * Authors:
12 * Bob Jamison <ishmal@inkscape.org>
13 *
14 * Copyright (C) 2004-2008 Authors
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 #include "pov-out.h"
24 #include <inkscape.h>
25 #include <inkscape_version.h>
26 #include <sp-path.h>
27 #include <style.h>
28 #include <display/curve.h>
29 #include <libnr/n-art-bpath.h>
30 #include <extension/system.h>
32 #include <io/sys.h>
34 #include <string>
35 #include <stdio.h>
36 #include <stdarg.h>
39 namespace Inkscape
40 {
41 namespace Extension
42 {
43 namespace Internal
44 {
49 //########################################################################
50 //# U T I L I T Y
51 //########################################################################
55 /**
56 * This function searches the Repr tree recursively from the given node,
57 * and adds refs to all nodes with the given name, to the result vector
58 */
59 static void
60 findElementsByTagName(std::vector<Inkscape::XML::Node *> &results,
61 Inkscape::XML::Node *node,
62 char const *name)
63 {
64 if ( !name || strcmp(node->name(), name) == 0 )
65 results.push_back(node);
67 for (Inkscape::XML::Node *child = node->firstChild() ; child ;
68 child = child->next())
69 findElementsByTagName( results, child, name );
71 }
77 static double
78 effective_opacity(SPItem const *item)
79 {
80 double ret = 1.0;
81 for (SPObject const *obj = item; obj; obj = obj->parent)
82 {
83 SPStyle const *const style = SP_OBJECT_STYLE(obj);
84 g_return_val_if_fail(style, ret);
85 ret *= SP_SCALE24_TO_FLOAT(style->opacity.value);
86 }
87 return ret;
88 }
94 //########################################################################
95 //# OUTPUT FORMATTING
96 //########################################################################
99 /**
100 * We want to control floating output format
101 */
102 static PovOutput::String dstr(double d)
103 {
104 char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
105 g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
106 "%.8f", (gdouble)d);
107 PovOutput::String s = dbuf;
108 return s;
109 }
114 /**
115 * Output data to the buffer, printf()-style
116 */
117 void PovOutput::out(const char *fmt, ...)
118 {
119 va_list args;
120 va_start(args, fmt);
121 gchar *output = g_strdup_vprintf(fmt, args);
122 va_end(args);
123 outbuf.append(output);
124 g_free(output);
125 }
131 /**
132 * Output a 2d vector
133 */
134 void PovOutput::vec2(double a, double b)
135 {
136 out("<%s, %s>", dstr(a).c_str(), dstr(b).c_str());
137 }
141 /**
142 * Output a 3d vector
143 */
144 void PovOutput::vec3(double a, double b, double c)
145 {
146 out("<%s, %s, %s>", dstr(a).c_str(), dstr(b).c_str(), dstr(c).c_str());
147 }
151 /**
152 * Output a v4d ector
153 */
154 void PovOutput::vec4(double a, double b, double c, double d)
155 {
156 out("<%s, %s, %s, %s>", dstr(a).c_str(), dstr(b).c_str(),
157 dstr(c).c_str(), dstr(d).c_str());
158 }
162 /**
163 * Output an rgbf color vector
164 */
165 void PovOutput::rgbf(double r, double g, double b, double f)
166 {
167 //"rgbf < %1.3f, %1.3f, %1.3f %1.3f>"
168 out("rgbf ");
169 vec4(r, g, b, f);
170 }
174 /**
175 * Output one bezier's start, start-control, end-control, and end nodes
176 */
177 void PovOutput::segment(int segNr,
178 double startX, double startY,
179 double startCtrlX, double startCtrlY,
180 double endCtrlX, double endCtrlY,
181 double endX, double endY)
182 {
183 //" /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>"
184 out(" /*%4d*/ ", segNr);
185 vec2(startX, startY);
186 out(", ");
187 vec2(startCtrlX, startCtrlY);
188 out(", ");
189 vec2(endCtrlX, endCtrlY);
190 out(", ");
191 vec2(endX, endY);
192 }
198 /**
199 * Output the file header
200 */
201 void PovOutput::doHeader()
202 {
203 time_t tim = time(NULL);
204 out("/*###################################################################\n");
205 out("### This PovRay document was generated by Inkscape\n");
206 out("### http://www.inkscape.org\n");
207 out("### Created: %s", ctime(&tim));
208 out("### Version: %s\n", INKSCAPE_VERSION);
209 out("#####################################################################\n");
210 out("### NOTES:\n");
211 out("### ============\n");
212 out("### POVRay information can be found at\n");
213 out("### http://www.povray.org\n");
214 out("###\n");
215 out("### The 'AllShapes' objects at the bottom are provided as a\n");
216 out("### preview of how the output would look in a trace. However,\n");
217 out("### the main intent of this file is to provide the individual\n");
218 out("### shapes for inclusion in a POV project.\n");
219 out("###\n");
220 out("### For an example of how to use this file, look at\n");
221 out("### share/examples/istest.pov\n");
222 out("###\n");
223 out("### If you have any problems with this output, please see the\n");
224 out("### Inkscape project at http://www.inkscape.org, or visit\n");
225 out("### the #inkscape channel on irc.freenode.net . \n");
226 out("###\n");
227 out("###################################################################*/\n");
228 out("\n\n");
229 out("/*###################################################################\n");
230 out("## Exports in this file\n");
231 out("##==========================\n");
232 out("## Shapes : %d\n", nrShapes);
233 out("## Segments : %d\n", nrSegments);
234 out("## Nodes : %d\n", nrNodes);
235 out("###################################################################*/\n");
236 out("\n\n\n");
237 }
241 /**
242 * Output the file footer
243 */
244 void PovOutput::doTail()
245 {
246 out("\n\n");
247 out("/*###################################################################\n");
248 out("### E N D F I L E\n");
249 out("###################################################################*/\n");
250 out("\n\n");
251 }
255 /**
256 * Output the curve data to buffer
257 */
258 void PovOutput::doCurves(SPDocument *doc)
259 {
260 std::vector<Inkscape::XML::Node *>results;
261 //findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, "path");
262 findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);
263 if (results.size() == 0)
264 return;
266 double bignum = 1000000.0;
267 double minx = bignum;
268 double maxx = -bignum;
269 double miny = bignum;
270 double maxy = -bignum;
272 for (unsigned int indx = 0; indx < results.size() ; indx++)
273 {
274 //### Fetch the object from the repr info
275 Inkscape::XML::Node *rpath = results[indx];
276 char *str = (char *) rpath->attribute("id");
277 if (!str)
278 continue;
280 String id = str;
281 SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath);
282 if (!reprobj)
283 continue;
285 //### Get the transform of the item
286 if (!SP_IS_ITEM(reprobj))
287 continue;
289 SPItem *item = SP_ITEM(reprobj);
290 NR::Matrix tf = from_2geom(sp_item_i2d_affine(item));
292 //### Get the Shape
293 if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion. Allow all shapes
294 continue;
296 SPShape *shape = SP_SHAPE(reprobj);
297 SPCurve *curve = shape->curve;
298 if (curve->is_empty())
299 continue;
301 nrShapes++;
303 PovShapeInfo shapeInfo;
304 shapeInfo.id = id;
305 shapeInfo.color = "";
307 //Try to get the fill color of the shape
308 SPStyle *style = SP_OBJECT_STYLE(shape);
309 /* fixme: Handle other fill types, even if this means translating gradients to a single
310 flat colour. */
311 if (style && (style->fill.isColor()))
312 {
313 // see color.h for how to parse SPColor
314 float rgb[3];
315 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
316 double const dopacity = ( SP_SCALE24_TO_FLOAT(style->fill_opacity.value)
317 * effective_opacity(shape) );
318 //gchar *str = g_strdup_printf("rgbf < %1.3f, %1.3f, %1.3f %1.3f>",
319 // rgb[0], rgb[1], rgb[2], 1.0 - dopacity);
320 String rgbf = "rgbf <";
321 rgbf.append(dstr(rgb[0])); rgbf.append(", ");
322 rgbf.append(dstr(rgb[1])); rgbf.append(", ");
323 rgbf.append(dstr(rgb[2])); rgbf.append(", ");
324 rgbf.append(dstr(1.0 - dopacity)); rgbf.append(">");
325 shapeInfo.color += rgbf;
326 }
328 povShapes.push_back(shapeInfo); //passed all tests. save the info
330 int curveLength = SP_CURVE_LENGTH(curve);
332 //Count the NR_CURVETOs/LINETOs
333 int segmentCount=0;
334 NArtBpath const *bp = curve->get_bpath();
335 for (int curveNr=0 ; curveNr<curveLength ; curveNr++, bp++)
336 if (bp->code == NR_CURVETO || bp->code == NR_LINETO)
337 segmentCount++;
339 double cminx = bignum;
340 double cmaxx = -bignum;
341 double cminy = bignum;
342 double cmaxy = -bignum;
343 double lastx = 0.0;
344 double lasty = 0.0;
346 out("/*###################################################\n");
347 out("### PRISM: %s\n", id.c_str());
348 out("###################################################*/\n");
349 out("#declare %s = prism {\n", id.c_str());
350 out(" linear_sweep\n");
351 out(" bezier_spline\n");
352 out(" 1.0, //top\n");
353 out(" 0.0, //bottom\n");
354 out(" %d //nr points\n", segmentCount * 4);
355 int segmentNr = 0;
356 bp = curve->get_bpath();
358 nrSegments += curveLength;
360 for (int curveNr=0 ; curveNr < curveLength ; curveNr++)
361 {
362 using NR::X;
363 using NR::Y;
364 //transform points. note overloaded '*'
365 NR::Point const p1(bp->c(1) * tf);
366 NR::Point const p2(bp->c(2) * tf);
367 NR::Point const p3(bp->c(3) * tf);
368 double const x1 = p1[X], y1 = p1[Y];
369 double const x2 = p2[X], y2 = p2[Y];
370 double const x3 = p3[X], y3 = p3[Y];
372 switch (bp->code)
373 {
374 case NR_MOVETO:
375 case NR_MOVETO_OPEN:
376 {
377 //fprintf(f, "moveto: %f %f\n", bp->x3, bp->y3);
378 break;
379 }
380 case NR_CURVETO:
381 {
382 //fprintf(f, " /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>",
383 // segmentNr++, lastx, lasty, x1, y1, x2, y2, x3, y3);
384 segment(segmentNr++,
385 lastx, lasty, x1, y1, x2, y2, x3, y3);
386 nrNodes += 8;
388 if (segmentNr < segmentCount)
389 out(",\n");
390 else
391 out("\n");
393 if (lastx < cminx)
394 cminx = lastx;
395 if (lastx > cmaxx)
396 cmaxx = lastx;
397 if (lasty < cminy)
398 cminy = lasty;
399 if (lasty > cmaxy)
400 cmaxy = lasty;
401 break;
402 }
403 case NR_LINETO:
404 {
405 //NOTE: we need to carefully handle line->curve and curve->line
406 // transitions.
407 //fprintf(f, " /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>",
408 // segmentNr++, lastx, lasty, lastx, lasty, x3, y3, x3, y3);
409 segment(segmentNr++,
410 lastx, lasty, lastx, lasty, x3, y3, x3, y3);
411 nrNodes += 8;
413 if (segmentNr < segmentCount)
414 out(",\n");
415 else
416 out("\n");
418 //fprintf(f, "lineto\n");
419 if (lastx < cminx)
420 cminx = lastx;
421 if (lastx > cmaxx)
422 cmaxx = lastx;
423 if (lasty < cminy)
424 cminy = lasty;
425 if (lasty > cmaxy)
426 cmaxy = lasty;
427 break;
428 }
429 case NR_END:
430 {
431 //fprintf(f, "end\n");
432 break;
433 }
434 }
435 lastx = x3;
436 lasty = y3;
437 bp++;
438 }
439 out("}\n");
442 //# prefix for following declarations
443 char *pfx = (char *)id.c_str();
445 out("#declare %s_MIN_X = %s;\n", pfx, dstr(cminx).c_str());
446 out("#declare %s_CENTER_X = %s;\n", pfx, dstr((cmaxx+cminx)/2.0).c_str());
447 out("#declare %s_MAX_X = %s;\n", pfx, dstr(cmaxx).c_str());
448 out("#declare %s_WIDTH = %s;\n", pfx, dstr(cmaxx-cminx).c_str());
449 out("#declare %s_MIN_Y = %s;\n", pfx, dstr(cminy).c_str());
450 out("#declare %s_CENTER_Y = %s;\n", pfx, dstr((cmaxy+cminy)/2.0).c_str());
451 out("#declare %s_MAX_Y = %s;\n", pfx, dstr(cmaxy).c_str());
452 out("#declare %s_HEIGHT = %s;\n", pfx, dstr(cmaxy-cminy).c_str());
453 if (shapeInfo.color.length()>0)
454 out("#declare %s_COLOR = %s;\n",
455 pfx, shapeInfo.color.c_str());
456 out("/*###################################################\n");
457 out("### end %s\n", id.c_str());
458 out("###################################################*/\n\n\n\n");
459 if (cminx < minx)
460 minx = cminx;
461 if (cmaxx > maxx)
462 maxx = cmaxx;
463 if (cminy < miny)
464 miny = cminy;
465 if (cmaxy > maxy)
466 maxy = cmaxy;
468 }//for
472 //## Let's make a union of all of the Shapes
473 if (povShapes.size()>0)
474 {
475 String id = "AllShapes";
476 char *pfx = (char *)id.c_str();
477 out("/*###################################################\n");
478 out("### UNION OF ALL SHAPES IN DOCUMENT\n");
479 out("###################################################*/\n");
480 out("\n\n");
481 out("/**\n");
482 out(" * Allow the user to redefine the finish{}\n");
483 out(" * by declaring it before #including this file\n");
484 out(" */\n");
485 out("#ifndef (%s_Finish)\n", pfx);
486 out("#declare %s_Finish = finish {\n", pfx);
487 out(" phong 0.5\n");
488 out(" reflection 0.3\n");
489 out(" specular 0.5\n");
490 out("}\n");
491 out("#end\n");
492 out("\n\n");
493 out("#declare %s = union {\n", id.c_str());
494 for (unsigned i = 0 ; i < povShapes.size() ; i++)
495 {
496 out(" object { %s\n", povShapes[i].id.c_str());
497 out(" texture { \n");
498 if (povShapes[i].color.length()>0)
499 out(" pigment { %s }\n", povShapes[i].color.c_str());
500 else
501 out(" pigment { rgb <0,0,0> }\n");
502 out(" finish { %s_Finish }\n", pfx);
503 out(" } \n");
504 out(" } \n");
505 }
506 out("}\n\n\n\n");
509 double zinc = 0.2 / (double)povShapes.size();
510 out("/*#### Same union, but with Z-diffs (actually Y in pov) ####*/\n");
511 out("\n\n");
512 out("/**\n");
513 out(" * Allow the user to redefine the Z-Increment\n");
514 out(" */\n");
515 out("#ifndef (AllShapes_Z_Increment)\n");
516 out("#declare AllShapes_Z_Increment = %s;\n", dstr(zinc).c_str());
517 out("#end\n");
518 out("\n");
519 out("#declare AllShapes_Z_Scale = 1.0;\n");
520 out("\n\n");
521 out("#declare %s_Z = union {\n", pfx);
523 for (unsigned i = 0 ; i < povShapes.size() ; i++)
524 {
525 out(" object { %s\n", povShapes[i].id.c_str());
526 out(" texture { \n");
527 if (povShapes[i].color.length()>0)
528 out(" pigment { %s }\n", povShapes[i].color.c_str());
529 else
530 out(" pigment { rgb <0,0,0> }\n");
531 out(" finish { %s_Finish }\n", pfx);
532 out(" } \n");
533 out(" scale <1, %s_Z_Scale, 1>\n", pfx);
534 out(" } \n");
535 out("#declare %s_Z_Scale = %s_Z_Scale + %s_Z_Increment;\n\n",
536 pfx, pfx, pfx);
537 }
539 out("}\n");
541 out("#declare %s_MIN_X = %s;\n", pfx, dstr(minx).c_str());
542 out("#declare %s_CENTER_X = %s;\n", pfx, dstr((maxx+minx)/2.0).c_str());
543 out("#declare %s_MAX_X = %s;\n", pfx, dstr(maxx).c_str());
544 out("#declare %s_WIDTH = %s;\n", pfx, dstr(maxx-minx).c_str());
545 out("#declare %s_MIN_Y = %s;\n", pfx, dstr(miny).c_str());
546 out("#declare %s_CENTER_Y = %s;\n", pfx, dstr((maxy+miny)/2.0).c_str());
547 out("#declare %s_MAX_Y = %s;\n", pfx, dstr(maxy).c_str());
548 out("#declare %s_HEIGHT = %s;\n", pfx, dstr(maxy-miny).c_str());
549 out("/*##############################################\n");
550 out("### end %s\n", id.c_str());
551 out("##############################################*/\n");
552 out("\n\n");
553 }
555 }
560 //########################################################################
561 //# M A I N O U T P U T
562 //########################################################################
566 /**
567 * Set values back to initial state
568 */
569 void PovOutput::reset()
570 {
571 nrNodes = 0;
572 nrSegments = 0;
573 nrShapes = 0;
574 outbuf.clear();
575 povShapes.clear();
576 }
580 /**
581 * Saves the <paths> of an Inkscape SVG file as PovRay spline definitions
582 */
583 void PovOutput::saveDocument(SPDocument *doc, gchar const *uri)
584 {
585 reset();
587 //###### SAVE IN POV FORMAT TO BUFFER
588 //# Lets do the curves first, to get the stats
589 doCurves(doc);
590 String curveBuf = outbuf;
591 outbuf.clear();
593 doHeader();
595 outbuf.append(curveBuf);
597 doTail();
602 //###### WRITE TO FILE
603 Inkscape::IO::dump_fopen_call(uri, "L");
604 FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");
605 if (!f)
606 return;
608 for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)
609 {
610 int ch = *iter;
611 fputc(ch, f);
612 }
614 fclose(f);
615 }
620 //########################################################################
621 //# EXTENSION API
622 //########################################################################
626 #include "clear-n_.h"
630 /**
631 * API call to save document
632 */
633 void
634 PovOutput::save(Inkscape::Extension::Output *mod,
635 SPDocument *doc, gchar const *uri)
636 {
637 saveDocument(doc, uri);
638 }
642 /**
643 * Make sure that we are in the database
644 */
645 bool PovOutput::check (Inkscape::Extension::Extension *module)
646 {
647 /* We don't need a Key
648 if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
649 return FALSE;
650 */
652 return true;
653 }
657 /**
658 * This is the definition of PovRay output. This function just
659 * calls the extension system with the memory allocated XML that
660 * describes the data.
661 */
662 void
663 PovOutput::init()
664 {
665 Inkscape::Extension::build_from_mem(
666 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
667 "<name>" N_("PovRay Output") "</name>\n"
668 "<id>org.inkscape.output.pov</id>\n"
669 "<output>\n"
670 "<extension>.pov</extension>\n"
671 "<mimetype>text/x-povray-script</mimetype>\n"
672 "<filetypename>" N_("PovRay (*.pov) (export splines)") "</filetypename>\n"
673 "<filetypetooltip>" N_("PovRay Raytracer File") "</filetypetooltip>\n"
674 "</output>\n"
675 "</inkscape-extension>",
676 new PovOutput());
677 }
683 } // namespace Internal
684 } // namespace Extension
685 } // namespace Inkscape
688 /*
689 Local Variables:
690 mode:c++
691 c-file-style:"stroustrup"
692 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
693 indent-tabs-mode:nil
694 fill-column:99
695 End:
696 */
697 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :