Code

8e224b4dcad5eedaab80fc5eb39c20f8c1be1908
[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 <ishmalius@gmail.com>
13  *
14  * Copyright (C) 2004-2007 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>
37 namespace Inkscape
38 {
39 namespace Extension
40 {
41 namespace Internal
42 {
44 typedef std::string String;
47 /**
48  * This function searches the Repr tree recursively from the given node,
49  * and adds refs to all nodes with the given name, to the result vector
50  */
51 static void
52 findElementsByTagName(std::vector<Inkscape::XML::Node *> &results,
53                       Inkscape::XML::Node *node,
54                       char const *name)
55 {
56     if ( !name || strcmp(node->name(), name) == 0 )
57         results.push_back(node);
59     for (Inkscape::XML::Node *child = node->firstChild() ; child ;
60               child = child->next())
61         findElementsByTagName( results, child, name );
63 }
66 /**
67  * used for saving information about shapes
68  */
69 class PovShapeInfo
70 {
71 public:
72     PovShapeInfo()
73         {}
74     PovShapeInfo(const PovShapeInfo &other)
75         { assign(other); }
76     PovShapeInfo operator=(const PovShapeInfo &other)
77         { assign(other); return *this; }
78     virtual ~PovShapeInfo()
79         {}
80     String id;
81     String color;
83 private:
84     void assign(const PovShapeInfo &other)
85         {
86         id    = other.id;
87         color = other.color;
88         }
89 };
93 static double
94 effective_opacity(SPItem const *item)
95 {
96     double ret = 1.0;
97     for (SPObject const *obj = item; obj; obj = obj->parent)
98         {
99         SPStyle const *const style = SP_OBJECT_STYLE(obj);
100         g_return_val_if_fail(style, ret);
101         ret *= SP_SCALE24_TO_FLOAT(style->opacity.value);
102         }
103     return ret;
107 //########################################################################
108 //# OUTPUT FORMATTING
109 //########################################################################
111 static const char *formatDouble(gchar *sbuffer, double d)
113     return (const char *)g_ascii_formatd(sbuffer,
114                  G_ASCII_DTOSTR_BUF_SIZE, "%.8g", (gdouble)d);
119 /**
120  * Not-threadsafe version
121  */
122 static char _dstr_buf[G_ASCII_DTOSTR_BUF_SIZE+1];
124 static const char *dstr(double d)
126     return formatDouble(_dstr_buf, d);
132 static String vec2Str(double a, double b)
134     String str;
135     str.append("<");
136     str.append(dstr(a));
137     str.append(", ");
138     str.append(dstr(b));
139     str.append(">");
140     return str;
143 /*
144 static String vec3Str(double a, double b, double c)
146     String str;
147     str.append("<");
148     str.append(dstr(a));
149     str.append(", ");
150     str.append(dstr(b));
151     str.append(", ");
152     str.append(dstr(c));
153     str.append(">");
154     return str;
156 */
158 static String vec4Str(double a, double b, double c, double d)
160     String str;
161     str.append("<");
162     str.append(dstr(a));
163     str.append(", ");
164     str.append(dstr(b));
165     str.append(", ");
166     str.append(dstr(c));
167     str.append(", ");
168     str.append(dstr(d));
169     str.append(">");
170     return str;
174 static String formatRgbf(double r, double g, double b, double f)
176     //"rgbf < %1.3f, %1.3f, %1.3f %1.3f>"
177     String str;
178     str.append("rgbf ");
179     str.append(vec4Str(r, g, b, f));
180     return str;
183 static String formatSeg(int segNr, double a0, double a1,
184                             double b0, double b1,
185                             double c0, double c1,
186                             double d0, double d1)
188     //"    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>"
189     String str;
190     char buf[32];
191     snprintf(buf, 31, "    /*%4d*/ ", segNr);
192     str.append(buf);
193     str.append(vec2Str(a0, a1));
194     str.append(", ");
195     str.append(vec2Str(b0, b1));
196     str.append(", ");
197     str.append(vec2Str(c0, c1));
198     str.append(", ");
199     str.append(vec2Str(d0, d1));
200     return str;
208 //########################################################################
209 //# M A I N    O U T P U T
210 //########################################################################
213 /**
214  * Saves the <paths> of an Inkscape SVG file as PovRay spline definitions
215 */
216 void
217 PovOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *uri)
219     std::vector<Inkscape::XML::Node *>results;
220     //findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, "path");
221     findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);//Check all nodes
222     if (results.size() == 0)
223         return;
225     Inkscape::IO::dump_fopen_call(uri, "L");
226     FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");
227     if (!f)
228         return;
230     time_t tim = time(NULL);
231     fprintf(f, "/*###################################################################\n");
232     fprintf(f, "### This PovRay document was generated by Inkscape\n");
233     fprintf(f, "### http://www.inkscape.org\n");
234     fprintf(f, "### Created: %s", ctime(&tim));
235     fprintf(f, "### Version: %s\n", VERSION);
236     fprintf(f, "#####################################################################\n");
237     fprintf(f, "### NOTES:\n");
238     fprintf(f, "### ============\n");
239     fprintf(f, "### POVRay information can be found at\n");
240     fprintf(f, "### http://www.povray.org\n");
241     fprintf(f, "###\n");
242     fprintf(f, "### The 'AllShapes' objects at the bottom are provided as a\n");
243     fprintf(f, "### preview of how the output would look in a trace.  However,\n");
244     fprintf(f, "### the main intent of this file is to provide the individual\n");
245     fprintf(f, "### shapes for inclusion in a POV project.\n");
246     fprintf(f, "###\n");
247     fprintf(f, "### For an example of how to use this file, look at\n");
248     fprintf(f, "### share/examples/istest.pov\n");
249     fprintf(f, "####################################################################*/\n\n\n");
251     //A list for saving information about the shapes
252     std::vector<PovShapeInfo>povShapes;
254     double bignum = 1000000.0;
255     double minx  =  bignum;
256     double maxx  = -bignum;
257     double miny  =  bignum;
258     double maxy  = -bignum;
260     for (unsigned int indx = 0; indx < results.size() ; indx++)
261         {
262         //### Fetch the object from the repr info
263         Inkscape::XML::Node *rpath = results[indx];
264         char *str  = (char *) rpath->attribute("id");
265         if (!str)
266             continue;
268         String id = str;
269         SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath);
270         if (!reprobj)
271             continue;
273         //### Get the transform of the item
274         if (!SP_IS_ITEM(reprobj))
275             continue;
277         SPItem *item = SP_ITEM(reprobj);
278         NR::Matrix tf = sp_item_i2d_affine(item);
280         //### Get the Shape
281         if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion.  Allow all shapes
282             continue;
284         SPShape *shape = SP_SHAPE(reprobj);
285         SPCurve *curve = shape->curve;
286         if (sp_curve_empty(curve))
287             continue;
289         PovShapeInfo shapeInfo;
290         shapeInfo.id    = id;
291         shapeInfo.color = "";
293         //Try to get the fill color of the shape
294         SPStyle *style = SP_OBJECT_STYLE(shape);
295         /* fixme: Handle other fill types, even if this means translating gradients to a single
296            flat colour. */
297         if (style && (style->fill.type == SP_PAINT_TYPE_COLOR))
298             {
299             // see color.h for how to parse SPColor
300             float rgb[3];
301             sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
302             double const dopacity = ( SP_SCALE24_TO_FLOAT(style->fill_opacity.value)
303                                       * effective_opacity(shape) );
304             //gchar *str = g_strdup_printf("rgbf < %1.3f, %1.3f, %1.3f %1.3f>",
305             //                             rgb[0], rgb[1], rgb[2], 1.0 - dopacity);
306             shapeInfo.color += formatRgbf(rgb[0], rgb[1], rgb[2], 1.0 - dopacity);
307             }
309         povShapes.push_back(shapeInfo); //passed all tests.  save the info
311         int curveLength = SP_CURVE_LENGTH(curve);
313         //Count the NR_CURVETOs/LINETOs
314         int segmentCount=0;
315         NArtBpath *bp = SP_CURVE_BPATH(curve);
316         for (int curveNr=0 ; curveNr<curveLength ; curveNr++, bp++)
317             if (bp->code == NR_CURVETO || bp->code == NR_LINETO)
318                 segmentCount++;
320         double cminx  =  bignum;
321         double cmaxx  = -bignum;
322         double cminy  =  bignum;
323         double cmaxy  = -bignum;
324         double lastx  = 0.0;
325         double lasty  = 0.0;
327         fprintf(f, "/*###################################################\n");
328         fprintf(f, "### PRISM:  %s\n", id.c_str());
329         fprintf(f, "###################################################*/\n");
330         fprintf(f, "#declare %s = prism {\n", id.c_str());
331         fprintf(f, "    linear_sweep\n");
332         fprintf(f, "    bezier_spline\n");
333         fprintf(f, "    1.0, //top\n");
334         fprintf(f, "    0.0, //bottom\n");
335         fprintf(f, "    %d, //nr points\n", segmentCount * 4);
336         int segmentNr = 0;
337         bp = SP_CURVE_BPATH(curve);
338         for (int curveNr=0 ; curveNr < curveLength ; curveNr++)
339             {
340             using NR::X;
341             using NR::Y;
342             NR::Point const p1(bp->c(1) * tf);
343             NR::Point const p2(bp->c(2) * tf);
344             NR::Point const p3(bp->c(3) * tf);
345             double const x1 = p1[X], y1 = p1[Y];
346             double const x2 = p2[X], y2 = p2[Y];
347             double const x3 = p3[X], y3 = p3[Y];
349             switch (bp->code)
350                 {
351                 case NR_MOVETO:
352                 case NR_MOVETO_OPEN:
353                     {
354                     //fprintf(f, "moveto: %f %f\n", bp->x3, bp->y3);
355                     break;
356                     }
357                 case NR_CURVETO:
358                     {
359                     //fprintf(f, "    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>",
360                     //        segmentNr++, lastx, lasty, x1, y1, x2, y2, x3, y3);
361                     String seg = formatSeg(segmentNr++,
362                                lastx, lasty, x1, y1, x2, y2, x3, y3);
363                     fprintf(f, "%s", seg.c_str());
365                     if (segmentNr < segmentCount)
366                         fprintf(f, ",\n");
367                     else
368                         fprintf(f, "\n");
370                     if (lastx < cminx)
371                         cminx = lastx;
372                     if (lastx > cmaxx)
373                         cmaxx = lastx;
374                     if (lasty < cminy)
375                         cminy = lasty;
376                     if (lasty > cmaxy)
377                         cmaxy = lasty;
378                     break;
379                     }
380                 case NR_LINETO:
381                     {
382                     //fprintf(f, "    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>",
383                     //        segmentNr++, lastx, lasty, lastx, lasty, x3, y3, x3, y3);
384                     String seg = formatSeg(segmentNr++,
385                                lastx, lasty, lastx, lasty, x3, y3, x3, y3);
386                     fprintf(f, "%s", seg.c_str());
388                     if (segmentNr < segmentCount)
389                         fprintf(f, ",\n");
390                     else
391                         fprintf(f, "\n");
393                     //fprintf(f, "lineto\n");
394                     if (lastx < cminx)
395                         cminx = lastx;
396                     if (lastx > cmaxx)
397                         cmaxx = lastx;
398                     if (lasty < cminy)
399                         cminy = lasty;
400                     if (lasty > cmaxy)
401                         cmaxy = lasty;
402                     break;
403                     }
404                 case NR_END:
405                     {
406                     //fprintf(f, "end\n");
407                     break;
408                     }
409                 }
410             lastx = x3;
411             lasty = y3;
412             bp++;
413             }
414         fprintf(f, "}\n");
417             char *pfx = (char *)id.c_str();
419         fprintf(f, "#declare %s_MIN_X    = %s;\n", pfx, dstr(cminx));
420         fprintf(f, "#declare %s_CENTER_X = %s;\n", pfx, dstr((cmaxx+cminx)/2.0));
421         fprintf(f, "#declare %s_MAX_X    = %s;\n", pfx, dstr(cmaxx));
422         fprintf(f, "#declare %s_WIDTH    = %s;\n", pfx, dstr(cmaxx-cminx));
423         fprintf(f, "#declare %s_MIN_Y    = %s;\n", pfx, dstr(cminy));
424         fprintf(f, "#declare %s_CENTER_Y = %s;\n", pfx, dstr((cmaxy+cminy)/2.0));
425         fprintf(f, "#declare %s_MAX_Y    = %s;\n", pfx, dstr(cmaxy));
426         fprintf(f, "#declare %s_HEIGHT   = %s;\n", pfx, dstr(cmaxy-cminy));
427         if (shapeInfo.color.length()>0)
428             fprintf(f, "#declare %s_COLOR    = %s;\n",
429                     pfx, shapeInfo.color.c_str());
430         fprintf(f, "/*###################################################\n");
431         fprintf(f, "### end %s\n", id.c_str());
432         fprintf(f, "###################################################*/\n\n\n\n");
433         if (cminx < minx)
434             minx = cminx;
435         if (cmaxx > maxx)
436             maxx = cmaxx;
437         if (cminy < miny)
438             miny = cminy;
439         if (cmaxy > maxy)
440             maxy = cmaxy;
442         }//for
446     //## Let's make a union of all of the Shapes
447     if (povShapes.size()>0)
448         {
449         String id = "AllShapes";
450         char *pfx = (char *)id.c_str();
451         fprintf(f, "/*###################################################\n");
452         fprintf(f, "### UNION OF ALL SHAPES IN DOCUMENT\n");
453         fprintf(f, "###################################################*/\n");
454         fprintf(f, "\n\n");
455         fprintf(f, "/**\n");
456         fprintf(f, " * Allow the user to redefine the finish{}\n");
457         fprintf(f, " * by declaring it before #including this file\n");
458         fprintf(f, " */\n");
459         fprintf(f, "#ifndef (%s_Finish)\n", pfx);
460         fprintf(f, "#declare %s_Finish = finish {\n", pfx);
461         fprintf(f, "    phong 0.5\n");
462         fprintf(f, "    reflection 0.3\n");
463         fprintf(f, "    specular 0.5\n");
464         fprintf(f, "}\n");
465         fprintf(f, "#end\n");
466         fprintf(f, "\n\n");
467         fprintf(f, "#declare %s = union {\n", id.c_str());
468         for (unsigned i = 0 ; i < povShapes.size() ; i++)
469             {
470             fprintf(f, "    object { %s\n", povShapes[i].id.c_str());
471             fprintf(f, "        texture { \n");
472             if (povShapes[i].color.length()>0)
473                 fprintf(f, "            pigment { %s }\n", povShapes[i].color.c_str());
474             else
475                 fprintf(f, "            pigment { rgb <0,0,0> }\n");
476             fprintf(f, "            finish { %s_Finish }\n", pfx);
477             fprintf(f, "            } \n");
478             fprintf(f, "        } \n");
479             }
480         fprintf(f, "}\n\n\n\n");
483         double zinc   = 0.2 / (double)povShapes.size();
484         fprintf(f, "/*#### Same union, but with Z-diffs (actually Y in pov) ####*/\n");
485         fprintf(f, "\n\n");
486         fprintf(f, "/**\n");
487         fprintf(f, " * Allow the user to redefine the Z-Increment\n");
488         fprintf(f, " */\n");
489         fprintf(f, "#ifndef (AllShapes_Z_Increment)\n");
490         fprintf(f, "#declare AllShapes_Z_Increment = %s;\n", dstr(zinc));
491         fprintf(f, "#end\n");
492         fprintf(f, "\n");
493         fprintf(f, "#declare AllShapes_Z_Scale = 1.0;\n");
494         fprintf(f, "\n\n");
495         fprintf(f, "#declare %s_Z = union {\n", pfx);
497         for (unsigned i = 0 ; i < povShapes.size() ; i++)
498             {
499             fprintf(f, "    object { %s\n", povShapes[i].id.c_str());
500             fprintf(f, "        texture { \n");
501             if (povShapes[i].color.length()>0)
502                 fprintf(f, "            pigment { %s }\n", povShapes[i].color.c_str());
503             else
504                 fprintf(f, "            pigment { rgb <0,0,0> }\n");
505             fprintf(f, "            finish { %s_Finish }\n", pfx);
506             fprintf(f, "            } \n");
507             fprintf(f, "        scale <1, %s_Z_Scale, 1>\n", pfx);
508             fprintf(f, "        } \n");
509             fprintf(f, "#declare %s_Z_Scale = %s_Z_Scale + %s_Z_Increment;\n\n",
510                     pfx, pfx, pfx);
511             }
513         fprintf(f, "}\n");
515         fprintf(f, "#declare %s_MIN_X    = %s;\n", pfx, dstr(minx));
516         fprintf(f, "#declare %s_CENTER_X = %s;\n", pfx, dstr((maxx+minx)/2.0));
517         fprintf(f, "#declare %s_MAX_X    = %s;\n", pfx, dstr(maxx));
518         fprintf(f, "#declare %s_WIDTH    = %s;\n", pfx, dstr(maxx-minx));
519         fprintf(f, "#declare %s_MIN_Y    = %s;\n", pfx, dstr(miny));
520         fprintf(f, "#declare %s_CENTER_Y = %s;\n", pfx, dstr((maxy+miny)/2.0));
521         fprintf(f, "#declare %s_MAX_Y    = %s;\n", pfx, dstr(maxy));
522         fprintf(f, "#declare %s_HEIGHT   = %s;\n", pfx, dstr(maxy-miny));
523         fprintf(f, "/*##############################################\n");
524         fprintf(f, "### end %s\n", id.c_str());
525         fprintf(f, "##############################################*/\n\n\n\n");
526         }
528     //All done
529     fclose(f);
535 //########################################################################
536 //# EXTENSION API
537 //########################################################################
541 #include "clear-n_.h"
545 /**
546  * Make sure that we are in the database
547  */
548 bool PovOutput::check (Inkscape::Extension::Extension *module)
550     /* We don't need a Key
551     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
552         return FALSE;
553     */
555     return true;
560 /**
561  * This is the definition of PovRay output.  This function just
562  * calls the extension system with the memory allocated XML that
563  * describes the data.
564 */
565 void
566 PovOutput::init()
568     Inkscape::Extension::build_from_mem(
569         "<inkscape-extension>\n"
570             "<name>" N_("PovRay Output") "</name>\n"
571             "<id>org.inkscape.output.pov</id>\n"
572             "<output>\n"
573                 "<extension>.pov</extension>\n"
574                 "<mimetype>text/x-povray-script</mimetype>\n"
575                 "<filetypename>" N_("PovRay (*.pov) (export splines)") "</filetypename>\n"
576                 "<filetypetooltip>" N_("PovRay Raytracer File") "</filetypetooltip>\n"
577             "</output>\n"
578         "</inkscape-extension>",
579         new PovOutput());
586 }  //namespace Internal
587 }  //namespace Extension
588 }  //namespace Inkscape
591 /*
592   Local Variables:
593   mode:c++
594   c-file-style:"stroustrup"
595   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
596   indent-tabs-mode:nil
597   fill-column:99
598   End:
599 */
600 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :