Code

add todo comment to make code prettier
[inkscape.git] / src / extension / internal / pov-out.cpp
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)
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;
114 /**
115  *  Output data to the buffer, printf()-style
116  */
117 void PovOutput::out(const char *fmt, ...)
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);
131 /**
132  *  Output a 2d vector
133  */
134 void PovOutput::vec2(double a, double b)
136     out("<%s, %s>", dstr(a).c_str(), dstr(b).c_str());
141 /**
142  * Output a 3d vector
143  */
144 void PovOutput::vec3(double a, double b, double c)
146     out("<%s, %s, %s>", dstr(a).c_str(), dstr(b).c_str(), dstr(c).c_str());
151 /**
152  *  Output a v4d ector
153  */
154 void PovOutput::vec4(double a, double b, double c, double d)
156     out("<%s, %s, %s, %s>", dstr(a).c_str(), dstr(b).c_str(),
157                  dstr(c).c_str(), dstr(d).c_str());
162 /**
163  *  Output an rgbf color vector
164  */
165 void PovOutput::rgbf(double r, double g, double b, double f)
167     //"rgbf < %1.3f, %1.3f, %1.3f %1.3f>"
168     out("rgbf ");
169     vec4(r, g, b, f);
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)
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);
198 /**
199  * Output the file header
200  */
201 void PovOutput::doHeader()
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");
241 /**
242  *  Output the file footer
243  */
244 void PovOutput::doTail()
246     out("\n\n");
247     out("/*###################################################################\n");
248     out("### E N D    F I L E\n");
249     out("###################################################################*/\n");
250     out("\n\n");
255 /**
256  *  Output the curve data to buffer
257  */
258 void PovOutput::doCurves(SPDocument *doc)
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;
300             
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 = SP_CURVE_BPATH(curve);
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 = SP_CURVE_BPATH(curve);
357         
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         }
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()
571     nrNodes    = 0;
572     nrSegments = 0;
573     nrShapes   = 0;
574     outbuf.clear();
575     povShapes.clear();
580 /**
581  * Saves the <paths> of an Inkscape SVG file as PovRay spline definitions
582  */
583 void PovOutput::saveDocument(SPDocument *doc, gchar const *uri)
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();
594     
595     outbuf.append(curveBuf);
596     
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         }
613         
614     fclose(f);
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)
637     saveDocument(doc, uri);
642 /**
643  * Make sure that we are in the database
644  */
645 bool PovOutput::check (Inkscape::Extension::Extension *module)
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;
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()
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());
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 :