Code

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