Code

Cleanup public/private, doxygen comments
[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>
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 //########################################################################
97 static const char *formatDouble(gchar *sbuffer, double d)
98 {
99     return (const char *)g_ascii_formatd(sbuffer,
100                  G_ASCII_DTOSTR_BUF_SIZE, "%.8g", (gdouble)d);
105 /**
106  * Not-threadsafe version
107  */
108 static char _dstr_buf[G_ASCII_DTOSTR_BUF_SIZE+1];
110 static const char *dstr(double d)
112     return formatDouble(_dstr_buf, d);
120 /**
121  *  Output data to the buffer, printf()-style
122  */
123 void PovOutput::out(char *fmt, ...)
125     va_list args;
126     va_start(args, fmt);
127 #if !defined(__GNUC__) || defined(__MINGW32__)
128     vsnprintf(fmtbuf, 4096, fmt, args);
129 #else
130     vsprintf(fmtbuf, 4096, fmt, args);
131 #endif
132     va_end(args);
133     outbuf.append(fmtbuf);
142 /**
143  *  Output a 3d vector
144  */
145 void PovOutput::vec2(double a, double b)
147     outbuf.append("<");
148     outbuf.append(dstr(a));
149     outbuf.append(", ");
150     outbuf.append(dstr(b));
151     outbuf.append(">");
156 /**
157  * Output a 3d vector
158  */
159 void PovOutput::vec3(double a, double b, double c)
161     outbuf.append("<");
162     outbuf.append(dstr(a));
163     outbuf.append(", ");
164     outbuf.append(dstr(b));
165     outbuf.append(", ");
166     outbuf.append(dstr(c));
167     outbuf.append(">");
172 /**
173  *  Output a v4d ector
174  */
175 void PovOutput::vec4(double a, double b, double c, double d)
177     outbuf.append("<");
178     outbuf.append(dstr(a));
179     outbuf.append(", ");
180     outbuf.append(dstr(b));
181     outbuf.append(", ");
182     outbuf.append(dstr(c));
183     outbuf.append(", ");
184     outbuf.append(dstr(d));
185     outbuf.append(">");
189 /**
190  *  Output an rgbf color vector
191  */
192 void PovOutput::rgbf(double r, double g, double b, double f)
194     //"rgbf < %1.3f, %1.3f, %1.3f %1.3f>"
195     outbuf.append("rgbf ");
196     vec4(r, g, b, f);
201 /**
202  *  Output one bezier's start, start-control, end-control, and end nodes
203  */
204 void PovOutput::segment(int segNr, double a0, double a1,
205                             double b0, double b1,
206                             double c0, double c1,
207                             double d0, double d1)
209     //"    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>"
210     char buf[32];
211     snprintf(buf, 31, "    /*%4d*/ ", segNr);
212     outbuf.append(buf);
213     vec2(a0, a1);
214     outbuf.append(", ");
215     vec2(b0, b1);
216     outbuf.append(", ");
217     vec2(c0, c1);
218     outbuf.append(", ");
219     vec2(d0, d1);
226 /**
227  * Output the file header
228  */
229 void PovOutput::doHeader()
231     time_t tim = time(NULL);
232     out("/*###################################################################\n");
233     out("### This PovRay document was generated by Inkscape\n");
234     out("### http://www.inkscape.org\n");
235     out("### Created: %s", ctime(&tim));
236     out("### Version: %s\n", VERSION);
237     out("#####################################################################\n");
238     out("### NOTES:\n");
239     out("### ============\n");
240     out("### POVRay information can be found at\n");
241     out("### http://www.povray.org\n");
242     out("###\n");
243     out("### The 'AllShapes' objects at the bottom are provided as a\n");
244     out("### preview of how the output would look in a trace.  However,\n");
245     out("### the main intent of this file is to provide the individual\n");
246     out("### shapes for inclusion in a POV project.\n");
247     out("###\n");
248     out("### For an example of how to use this file, look at\n");
249     out("### share/examples/istest.pov\n");
250     out("###################################################################*/\n");
251     out("\n\n");
252     out("/*###################################################################\n");
253     out("##   Exports in this file\n");
254     out("##==========================\n");
255     out("##    Shapes   : %d\n", nrShapes);
256     out("##    Segments : %d\n", nrSegments);
257     out("##    Nodes    : %d\n", nrNodes);
258     out("###################################################################*/\n");
259     out("\n\n\n");
264 /**
265  *  Output the file footer
266  */
267 void PovOutput::doTail()
269     out("\n\n");
270     out("/*###################################################################\n");
271     out("### E N D    F I L E\n");
272     out("###################################################################*/\n");
273     out("\n\n");
278 /**
279  *  Output the curve data to buffer
280  */
281 void PovOutput::doCurves(SPDocument *doc)
283     std::vector<Inkscape::XML::Node *>results;
284     //findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, "path");
285     findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);
286     if (results.size() == 0)
287         return;
289     double bignum = 1000000.0;
290     double minx  =  bignum;
291     double maxx  = -bignum;
292     double miny  =  bignum;
293     double maxy  = -bignum;
295     for (unsigned int indx = 0; indx < results.size() ; indx++)
296         {
297         //### Fetch the object from the repr info
298         Inkscape::XML::Node *rpath = results[indx];
299         char *str  = (char *) rpath->attribute("id");
300         if (!str)
301             continue;
303         String id = str;
304         SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath);
305         if (!reprobj)
306             continue;
308         //### Get the transform of the item
309         if (!SP_IS_ITEM(reprobj))
310             continue;
312         SPItem *item = SP_ITEM(reprobj);
313         NR::Matrix tf = sp_item_i2d_affine(item);
315         //### Get the Shape
316         if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion.  Allow all shapes
317             continue;
319         SPShape *shape = SP_SHAPE(reprobj);
320         SPCurve *curve = shape->curve;
321         if (sp_curve_empty(curve))
322             continue;
323             
324         nrShapes++;
326         PovShapeInfo shapeInfo;
327         shapeInfo.id    = id;
328         shapeInfo.color = "";
330         //Try to get the fill color of the shape
331         SPStyle *style = SP_OBJECT_STYLE(shape);
332         /* fixme: Handle other fill types, even if this means translating gradients to a single
333            flat colour. */
334         if (style && (style->fill.type == SP_PAINT_TYPE_COLOR))
335             {
336             // see color.h for how to parse SPColor
337             float rgb[3];
338             sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
339             double const dopacity = ( SP_SCALE24_TO_FLOAT(style->fill_opacity.value)
340                                       * effective_opacity(shape) );
341             //gchar *str = g_strdup_printf("rgbf < %1.3f, %1.3f, %1.3f %1.3f>",
342             //                             rgb[0], rgb[1], rgb[2], 1.0 - dopacity);
343             String rgbf = "rgbf <";
344             rgbf.append(dstr(rgb[0]));         rgbf.append(", ");
345             rgbf.append(dstr(rgb[1]));         rgbf.append(", ");
346             rgbf.append(dstr(rgb[2]));         rgbf.append(", ");
347             rgbf.append(dstr(1.0 - dopacity)); rgbf.append(">");
348             shapeInfo.color += rgbf;
349             }
351         povShapes.push_back(shapeInfo); //passed all tests.  save the info
353         int curveLength = SP_CURVE_LENGTH(curve);
355         //Count the NR_CURVETOs/LINETOs
356         int segmentCount=0;
357         NArtBpath *bp = SP_CURVE_BPATH(curve);
358         for (int curveNr=0 ; curveNr<curveLength ; curveNr++, bp++)
359             if (bp->code == NR_CURVETO || bp->code == NR_LINETO)
360                 segmentCount++;
362         double cminx  =  bignum;
363         double cmaxx  = -bignum;
364         double cminy  =  bignum;
365         double cmaxy  = -bignum;
366         double lastx  = 0.0;
367         double lasty  = 0.0;
369         out("/*###################################################\n");
370         out("### PRISM:  %s\n", id.c_str());
371         out("###################################################*/\n");
372         out("#declare %s = prism {\n", id.c_str());
373         out("    linear_sweep\n");
374         out("    bezier_spline\n");
375         out("    1.0, //top\n");
376         out("    0.0, //bottom\n");
377         out("    %d //nr points\n", segmentCount * 4);
378         int segmentNr = 0;
379         bp = SP_CURVE_BPATH(curve);
380         
381         nrSegments += curveLength;
383         for (int curveNr=0 ; curveNr < curveLength ; curveNr++)
384             {
385             using NR::X;
386             using NR::Y;
387             NR::Point const p1(bp->c(1) * tf);
388             NR::Point const p2(bp->c(2) * tf);
389             NR::Point const p3(bp->c(3) * tf);
390             double const x1 = p1[X], y1 = p1[Y];
391             double const x2 = p2[X], y2 = p2[Y];
392             double const x3 = p3[X], y3 = p3[Y];
394             switch (bp->code)
395                 {
396                 case NR_MOVETO:
397                 case NR_MOVETO_OPEN:
398                     {
399                     //fprintf(f, "moveto: %f %f\n", bp->x3, bp->y3);
400                     break;
401                     }
402                 case NR_CURVETO:
403                     {
404                     //fprintf(f, "    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>",
405                     //        segmentNr++, lastx, lasty, x1, y1, x2, y2, x3, y3);
406                     segment(segmentNr++,
407                           lastx, lasty, x1, y1, x2, y2, x3, y3);
408                     nrNodes += 8;
410                     if (segmentNr < segmentCount)
411                         out(",\n");
412                     else
413                         out("\n");
415                     if (lastx < cminx)
416                         cminx = lastx;
417                     if (lastx > cmaxx)
418                         cmaxx = lastx;
419                     if (lasty < cminy)
420                         cminy = lasty;
421                     if (lasty > cmaxy)
422                         cmaxy = lasty;
423                     break;
424                     }
425                 case NR_LINETO:
426                     {
427                     //fprintf(f, "    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>",
428                     //        segmentNr++, lastx, lasty, lastx, lasty, x3, y3, x3, y3);
429                     segment(segmentNr++,
430                          lastx, lasty, lastx, lasty, x3, y3, x3, y3);
431                     nrNodes += 8;
433                     if (segmentNr < segmentCount)
434                         out(",\n");
435                     else
436                         out("\n");
438                     //fprintf(f, "lineto\n");
439                     if (lastx < cminx)
440                         cminx = lastx;
441                     if (lastx > cmaxx)
442                         cmaxx = lastx;
443                     if (lasty < cminy)
444                         cminy = lasty;
445                     if (lasty > cmaxy)
446                         cmaxy = lasty;
447                     break;
448                     }
449                 case NR_END:
450                     {
451                     //fprintf(f, "end\n");
452                     break;
453                     }
454                 }
455             lastx = x3;
456             lasty = y3;
457             bp++;
458             }
459         out("}\n");
462             char *pfx = (char *)id.c_str();
464         out("#declare %s_MIN_X    = %s;\n", pfx, dstr(cminx));
465         out("#declare %s_CENTER_X = %s;\n", pfx, dstr((cmaxx+cminx)/2.0));
466         out("#declare %s_MAX_X    = %s;\n", pfx, dstr(cmaxx));
467         out("#declare %s_WIDTH    = %s;\n", pfx, dstr(cmaxx-cminx));
468         out("#declare %s_MIN_Y    = %s;\n", pfx, dstr(cminy));
469         out("#declare %s_CENTER_Y = %s;\n", pfx, dstr((cmaxy+cminy)/2.0));
470         out("#declare %s_MAX_Y    = %s;\n", pfx, dstr(cmaxy));
471         out("#declare %s_HEIGHT   = %s;\n", pfx, dstr(cmaxy-cminy));
472         if (shapeInfo.color.length()>0)
473             out("#declare %s_COLOR    = %s;\n",
474                     pfx, shapeInfo.color.c_str());
475         out("/*###################################################\n");
476         out("### end %s\n", id.c_str());
477         out("###################################################*/\n\n\n\n");
478         if (cminx < minx)
479             minx = cminx;
480         if (cmaxx > maxx)
481             maxx = cmaxx;
482         if (cminy < miny)
483             miny = cminy;
484         if (cmaxy > maxy)
485             maxy = cmaxy;
487         }//for
491     //## Let's make a union of all of the Shapes
492     if (povShapes.size()>0)
493         {
494         String id = "AllShapes";
495         char *pfx = (char *)id.c_str();
496         out("/*###################################################\n");
497         out("### UNION OF ALL SHAPES IN DOCUMENT\n");
498         out("###################################################*/\n");
499         out("\n\n");
500         out("/**\n");
501         out(" * Allow the user to redefine the finish{}\n");
502         out(" * by declaring it before #including this file\n");
503         out(" */\n");
504         out("#ifndef (%s_Finish)\n", pfx);
505         out("#declare %s_Finish = finish {\n", pfx);
506         out("    phong 0.5\n");
507         out("    reflection 0.3\n");
508         out("    specular 0.5\n");
509         out("}\n");
510         out("#end\n");
511         out("\n\n");
512         out("#declare %s = union {\n", id.c_str());
513         for (unsigned i = 0 ; i < povShapes.size() ; i++)
514             {
515             out("    object { %s\n", povShapes[i].id.c_str());
516             out("        texture { \n");
517             if (povShapes[i].color.length()>0)
518                 out("            pigment { %s }\n", povShapes[i].color.c_str());
519             else
520                 out("            pigment { rgb <0,0,0> }\n");
521             out("            finish { %s_Finish }\n", pfx);
522             out("            } \n");
523             out("        } \n");
524             }
525         out("}\n\n\n\n");
528         double zinc   = 0.2 / (double)povShapes.size();
529         out("/*#### Same union, but with Z-diffs (actually Y in pov) ####*/\n");
530         out("\n\n");
531         out("/**\n");
532         out(" * Allow the user to redefine the Z-Increment\n");
533         out(" */\n");
534         out("#ifndef (AllShapes_Z_Increment)\n");
535         out("#declare AllShapes_Z_Increment = %s;\n", dstr(zinc));
536         out("#end\n");
537         out("\n");
538         out("#declare AllShapes_Z_Scale = 1.0;\n");
539         out("\n\n");
540         out("#declare %s_Z = union {\n", pfx);
542         for (unsigned i = 0 ; i < povShapes.size() ; i++)
543             {
544             out("    object { %s\n", povShapes[i].id.c_str());
545             out("        texture { \n");
546             if (povShapes[i].color.length()>0)
547                 out("            pigment { %s }\n", povShapes[i].color.c_str());
548             else
549                 out("            pigment { rgb <0,0,0> }\n");
550             out("            finish { %s_Finish }\n", pfx);
551             out("            } \n");
552             out("        scale <1, %s_Z_Scale, 1>\n", pfx);
553             out("        } \n");
554             out("#declare %s_Z_Scale = %s_Z_Scale + %s_Z_Increment;\n\n",
555                     pfx, pfx, pfx);
556             }
558         out("}\n");
560         out("#declare %s_MIN_X    = %s;\n", pfx, dstr(minx));
561         out("#declare %s_CENTER_X = %s;\n", pfx, dstr((maxx+minx)/2.0));
562         out("#declare %s_MAX_X    = %s;\n", pfx, dstr(maxx));
563         out("#declare %s_WIDTH    = %s;\n", pfx, dstr(maxx-minx));
564         out("#declare %s_MIN_Y    = %s;\n", pfx, dstr(miny));
565         out("#declare %s_CENTER_Y = %s;\n", pfx, dstr((maxy+miny)/2.0));
566         out("#declare %s_MAX_Y    = %s;\n", pfx, dstr(maxy));
567         out("#declare %s_HEIGHT   = %s;\n", pfx, dstr(maxy-miny));
568         out("/*##############################################\n");
569         out("### end %s\n", id.c_str());
570         out("##############################################*/\n");
571         out("\n\n");
572         }
579 //########################################################################
580 //# M A I N    O U T P U T
581 //########################################################################
585 /**
586  *  Set values back to initial state
587  */
588 void PovOutput::reset()
590     nrNodes    = 0;
591     nrSegments = 0;
592     nrShapes   = 0;
593     outbuf.clear();
594     povShapes.clear();
599 /**
600  * Saves the <paths> of an Inkscape SVG file as PovRay spline definitions
601  */
602 void PovOutput::saveDocument(SPDocument *doc, gchar const *uri)
604     reset();
606     //###### SAVE IN POV FORMAT TO BUFFER
607     //# Lets do the curves first, to get the stats
608     doCurves(doc);
609     String curveBuf = outbuf;
610     outbuf.clear();
612     doHeader();
613     
614     outbuf.append(curveBuf);
615     
616     doTail();
621     //###### WRITE TO FILE
622     Inkscape::IO::dump_fopen_call(uri, "L");
623     FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");
624     if (!f)
625         return;
627     for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)
628         {
629         int ch = *iter;
630         fputc(ch, f);
631         }
632         
633     fclose(f);
639 //########################################################################
640 //# EXTENSION API
641 //########################################################################
645 #include "clear-n_.h"
649 /**
650  * API call to save document
651 */
652 void
653 PovOutput::save(Inkscape::Extension::Output *mod,
654                         SPDocument *doc, gchar const *uri)
656     saveDocument(doc, uri);
661 /**
662  * Make sure that we are in the database
663  */
664 bool PovOutput::check (Inkscape::Extension::Extension *module)
666     /* We don't need a Key
667     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
668         return FALSE;
669     */
671     return true;
676 /**
677  * This is the definition of PovRay output.  This function just
678  * calls the extension system with the memory allocated XML that
679  * describes the data.
680 */
681 void
682 PovOutput::init()
684     Inkscape::Extension::build_from_mem(
685         "<inkscape-extension>\n"
686             "<name>" N_("PovRay Output") "</name>\n"
687             "<id>org.inkscape.output.pov</id>\n"
688             "<output>\n"
689                 "<extension>.pov</extension>\n"
690                 "<mimetype>text/x-povray-script</mimetype>\n"
691                 "<filetypename>" N_("PovRay (*.pov) (export splines)") "</filetypename>\n"
692                 "<filetypetooltip>" N_("PovRay Raytracer File") "</filetypetooltip>\n"
693             "</output>\n"
694         "</inkscape-extension>",
695         new PovOutput());
702 }  // namespace Internal
703 }  // namespace Extension
704 }  // namespace Inkscape
707 /*
708   Local Variables:
709   mode:c++
710   c-file-style:"stroustrup"
711   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
712   indent-tabs-mode:nil
713   fill-column:99
714   End:
715 */
716 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :