Code

A simple layout document as to what, why and how is cppification.
[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 <extension/system.h>
30 #include <2geom/pathvector.h>
31 #include <2geom/rect.h>
32 #include <2geom/bezier-curve.h>
33 #include <2geom/hvlinesegment.h>
34 #include "helper/geom.h"
35 #include "helper/geom-curves.h"
36 #include <io/sys.h>
38 #include <string>
39 #include <stdio.h>
40 #include <stdarg.h>
43 namespace Inkscape
44 {
45 namespace Extension
46 {
47 namespace Internal
48 {
51 //########################################################################
52 //# M E S S A G E S
53 //########################################################################
55 static void err(const char *fmt, ...)
56 {
57     va_list args;
58     g_log(NULL,  G_LOG_LEVEL_WARNING, "Pov-out err: ");
59     va_start(args, fmt);
60     g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);
61     va_end(args);
62     g_log(NULL,  G_LOG_LEVEL_WARNING, "\n");
63 }
68 //########################################################################
69 //# U T I L I T Y
70 //########################################################################
74 static double
75 effective_opacity(SPItem const *item)
76 {
77     double ret = 1.0;
78     for (SPObject const *obj = item; obj; obj = obj->parent)
79         {
80         SPStyle const *const style = SP_OBJECT_STYLE(obj);
81         g_return_val_if_fail(style, ret);
82         ret *= SP_SCALE24_TO_FLOAT(style->opacity.value);
83         }
84     return ret;
85 }
91 //########################################################################
92 //# OUTPUT FORMATTING
93 //########################################################################
96 /**
97  * We want to control floating output format
98  */
99 static PovOutput::String dstr(double d)
101     char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
102     g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
103                   "%.8f", (gdouble)d);
104     PovOutput::String s = dbuf;
105     return s;
108 #define DSTR(d) (dstr(d).c_str())
111 /**
112  *  Output data to the buffer, printf()-style
113  */
114 void PovOutput::out(const char *fmt, ...)
116     va_list args;
117     va_start(args, fmt);
118     gchar *output = g_strdup_vprintf(fmt, args);
119     va_end(args);
120     outbuf.append(output);
121     g_free(output);
128 /**
129  *  Output a 2d vector
130  */
131 void PovOutput::vec2(double a, double b)
133     out("<%s, %s>", DSTR(a), DSTR(b));
138 /**
139  * Output a 3d vector
140  */
141 void PovOutput::vec3(double a, double b, double c)
143     out("<%s, %s, %s>", DSTR(a), DSTR(b), DSTR(c));
148 /**
149  *  Output a v4d ector
150  */
151 void PovOutput::vec4(double a, double b, double c, double d)
153     out("<%s, %s, %s, %s>", DSTR(a), DSTR(b), DSTR(c), DSTR(d));
158 /**
159  *  Output an rgbf color vector
160  */
161 void PovOutput::rgbf(double r, double g, double b, double f)
163     //"rgbf < %1.3f, %1.3f, %1.3f %1.3f>"
164     out("rgbf ");
165     vec4(r, g, b, f);
170 /**
171  *  Output one bezier's start, start-control, end-control, and end nodes
172  */
173 void PovOutput::segment(int segNr,
174                         double startX,     double startY,
175                         double startCtrlX, double startCtrlY,
176                         double endCtrlX,   double endCtrlY,
177                         double endX,       double endY)
179     //"    /*%4d*/ <%f, %f>, <%f, %f>, <%f,%f>, <%f,%f>"
180     out("    /*%4d*/ ", segNr);
181     vec2(startX,     startY);
182     out(", ");
183     vec2(startCtrlX, startCtrlY);
184     out(", ");
185     vec2(endCtrlX,   endCtrlY);
186     out(", ");
187     vec2(endX,       endY);
194 /**
195  * Output the file header
196  */
197 bool PovOutput::doHeader()
199     time_t tim = time(NULL);
200     out("/*###################################################################\n");
201     out("### This PovRay document was generated by Inkscape\n");
202     out("### http://www.inkscape.org\n");
203     out("### Created: %s",   ctime(&tim));
204     out("### Version: %s\n", Inkscape::version_string);
205     out("#####################################################################\n");
206     out("### NOTES:\n");
207     out("### ============\n");
208     out("### POVRay information can be found at\n");
209     out("### http://www.povray.org\n");
210     out("###\n");
211     out("### The 'AllShapes' objects at the bottom are provided as a\n");
212     out("### preview of how the output would look in a trace.  However,\n");
213     out("### the main intent of this file is to provide the individual\n");
214     out("### shapes for inclusion in a POV project.\n");
215     out("###\n");
216     out("### For an example of how to use this file, look at\n");
217     out("### share/examples/istest.pov\n");
218     out("###\n");
219     out("### If you have any problems with this output, please see the\n");
220     out("### Inkscape project at http://www.inkscape.org, or visit\n");
221     out("### the #inkscape channel on irc.freenode.net . \n");
222     out("###\n");
223     out("###################################################################*/\n");
224     out("\n\n");
225     out("/*###################################################################\n");
226     out("##   Exports in this file\n");
227     out("##==========================\n");
228     out("##    Shapes   : %d\n", nrShapes);
229     out("##    Segments : %d\n", nrSegments);
230     out("##    Nodes    : %d\n", nrNodes);
231     out("###################################################################*/\n");
232     out("\n\n\n");
233     return true;
238 /**
239  *  Output the file footer
240  */
241 bool PovOutput::doTail()
243     out("\n\n");
244     out("/*###################################################################\n");
245     out("### E N D    F I L E\n");
246     out("###################################################################*/\n");
247     out("\n\n");
248     return true;
253 /**
254  *  Output the curve data to buffer
255  */
256 bool PovOutput::doCurve(SPItem *item, const String &id)
258     using Geom::X;
259     using Geom::Y;
261     //### Get the Shape
262     if (!SP_IS_SHAPE(item))//Bulia's suggestion.  Allow all shapes
263         return true;
265     SPShape *shape = SP_SHAPE(item);
266     SPCurve *curve = shape->curve;
267     if (curve->is_empty())
268         return true;
270     nrShapes++;
272     PovShapeInfo shapeInfo;
273     shapeInfo.id    = id;
274     shapeInfo.color = "";
276     //Try to get the fill color of the shape
277     SPStyle *style = SP_OBJECT_STYLE(shape);
278     /* fixme: Handle other fill types, even if this means translating gradients to a single
279            flat colour. */
280     if (style)
281         {
282         if (style->fill.isColor())
283             {
284             // see color.h for how to parse SPColor
285             float rgb[3];
286             sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
287             double const dopacity = ( SP_SCALE24_TO_FLOAT(style->fill_opacity.value)
288                                       * effective_opacity(shape) );
289             //gchar *str = g_strdup_printf("rgbf < %1.3f, %1.3f, %1.3f %1.3f>",
290             //                             rgb[0], rgb[1], rgb[2], 1.0 - dopacity);
291             String rgbf = "rgbf <";
292             rgbf.append(dstr(rgb[0]));         rgbf.append(", ");
293             rgbf.append(dstr(rgb[1]));         rgbf.append(", ");
294             rgbf.append(dstr(rgb[2]));         rgbf.append(", ");
295             rgbf.append(dstr(1.0 - dopacity)); rgbf.append(">");
296             shapeInfo.color += rgbf;
297             }
298         }
300     povShapes.push_back(shapeInfo); //passed all tests.  save the info
302     // convert the path to only lineto's and cubic curveto's:
303     Geom::Matrix tf = item->i2d_affine();
304     Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
306     /*
307      * We need to know the number of segments (NR_CURVETOs/LINETOs, including
308      * closing line segment) before we write out segment data. Since we are
309      * going to skip degenerate (zero length) paths, we need to loop over all
310      * subpaths and segments first.
311      */
312     int segmentCount = 0;
313     /**
314      * For all Subpaths in the <path>
315      */
316     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
317     {
318         /**
319          * For all segments in the subpath, including extra closing segment defined by 2geom
320          */
321         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
322         {
324             // Skip zero length segments.
325             if( !cit->isDegenerate() ) ++segmentCount;
326         }
327     }
329     out("/*###################################################\n");
330     out("### PRISM:  %s\n", id.c_str());
331     out("###################################################*/\n");
332     out("#declare %s = prism {\n", id.c_str());
333     out("    linear_sweep\n");
334     out("    bezier_spline\n");
335     out("    1.0, //top\n");
336     out("    0.0, //bottom\n");
337     out("    %d //nr points\n", segmentCount * 4);
338     int segmentNr = 0;
340     nrSegments += segmentCount;
342     /**
343      *   at moment of writing, 2geom lacks proper initialization of empty intervals in rect...
344      */
345     Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );
348     /**
349      * For all Subpaths in the <path>
350      */
351     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
352         {
354         cminmax.expandTo(pit->initialPoint());
356         /**
357          * For all segments in the subpath, including extra closing segment defined by 2geom
358          */
359         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
360             {
362             // Skip zero length segments
363             if( cit->isDegenerate() )
364                 continue;
366             if( is_straight_curve(*cit) )
367                 {
368                 Geom::Point p0 = cit->initialPoint();
369                 Geom::Point p1 = cit->finalPoint();
370                 segment(segmentNr++,
371                         p0[X], p0[Y], p0[X], p0[Y], p1[X], p1[Y], p1[X], p1[Y] );
372                 nrNodes += 8;
373                 }
374             else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
375             {
376                 std::vector<Geom::Point> points = cubic->points();
377                 Geom::Point p0 = points[0];
378                 Geom::Point p1 = points[1];
379                 Geom::Point p2 = points[2];
380                 Geom::Point p3 = points[3];
381                 segment(segmentNr++,
382                             p0[X],p0[Y], p1[X],p1[Y], p2[X],p2[Y], p3[X],p3[Y]);
383                 nrNodes += 8;
384                 }
385             else
386             {
387                 err("logical error, because pathv_to_linear_and_cubic_beziers was used");
388                 return false;
389                 }
391             if (segmentNr < segmentCount)
392                 out(",\n");
393             else
394                 out("\n");
395             if (segmentNr > segmentCount)
396                 {
397                 err("Too many segments");
398                 return false;
399                 }
401             cminmax.expandTo(cit->finalPoint());
403             }
404         }
406     out("}\n");
408     double cminx = cminmax.min()[X];
409     double cmaxx = cminmax.max()[X];
410     double cminy = cminmax.min()[Y];
411     double cmaxy = cminmax.max()[Y];
413     out("#declare %s_MIN_X    = %s;\n", id.c_str(), DSTR(cminx));
414     out("#declare %s_CENTER_X = %s;\n", id.c_str(), DSTR((cmaxx+cminx)/2.0));
415     out("#declare %s_MAX_X    = %s;\n", id.c_str(), DSTR(cmaxx));
416     out("#declare %s_WIDTH    = %s;\n", id.c_str(), DSTR(cmaxx-cminx));
417     out("#declare %s_MIN_Y    = %s;\n", id.c_str(), DSTR(cminy));
418     out("#declare %s_CENTER_Y = %s;\n", id.c_str(), DSTR((cmaxy+cminy)/2.0));
419     out("#declare %s_MAX_Y    = %s;\n", id.c_str(), DSTR(cmaxy));
420     out("#declare %s_HEIGHT   = %s;\n", id.c_str(), DSTR(cmaxy-cminy));
421     if (shapeInfo.color.length()>0)
422         out("#declare %s_COLOR    = %s;\n",
423                 id.c_str(), shapeInfo.color.c_str());
424     out("/*###################################################\n");
425     out("### end %s\n", id.c_str());
426     out("###################################################*/\n\n\n\n");
428     if (cminx < minx)
429         minx = cminx;
430     if (cmaxx > maxx)
431         maxx = cmaxx;
432     if (cminy < miny)
433         miny = cminy;
434     if (cmaxy > maxy)
435         maxy = cmaxy;
437     return true;
440 /**
441  *  Descend the svg tree recursively, translating data
442  */
443 bool PovOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)
446     String id;
447     if (!obj->getId())
448         {
449         char buf[16];
450         sprintf(buf, "id%d", idIndex++);
451         id = buf;
452         }
453     else
454         {
455             id = obj->getId();
456         }
458     if (SP_IS_ITEM(obj))
459         {
460         SPItem *item = SP_ITEM(obj);
461         if (!doCurve(item, id))
462             return false;
463         }
465     /**
466      * Descend into children
467      */
468     for (SPObject *child = obj->firstChild() ; child ; child = child->next)
469         {
470             if (!doTreeRecursive(doc, child))
471                 return false;
472         }
474     return true;
477 /**
478  *  Output the curve data to buffer
479  */
480 bool PovOutput::doTree(SPDocument *doc)
482     double bignum = 1000000.0;
483     minx  =  bignum;
484     maxx  = -bignum;
485     miny  =  bignum;
486     maxy  = -bignum;
488     if (!doTreeRecursive(doc, doc->root))
489         return false;
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         }
574     return true;
578 //########################################################################
579 //# M A I N    O U T P U T
580 //########################################################################
584 /**
585  *  Set values back to initial state
586  */
587 void PovOutput::reset()
589     nrNodes    = 0;
590     nrSegments = 0;
591     nrShapes   = 0;
592     idIndex    = 0;
593     outbuf.clear();
594     povShapes.clear();
599 /**
600  * Saves the Shapes of an Inkscape SVG file as PovRay spline definitions
601  */
602 void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
604     reset();
606     //###### SAVE IN POV FORMAT TO BUFFER
607     //# Lets do the curves first, to get the stats
608     if (!doTree(doc))
609         {
610         err("Could not output curves for %s", filename_utf8);
611         return;
612         }
614     String curveBuf = outbuf;
615     outbuf.clear();
617     if (!doHeader())
618         {
619         err("Could not write header for %s", filename_utf8);
620         return;
621         }
623     outbuf.append(curveBuf);
625     if (!doTail())
626         {
627         err("Could not write footer for %s", filename_utf8);
628         return;
629         }
634     //###### WRITE TO FILE
635     Inkscape::IO::dump_fopen_call(filename_utf8, "L");
636     FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
637     if (!f)
638         return;
640     for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)
641         {
642         int ch = *iter;
643         fputc(ch, f);
644         }
646     fclose(f);
652 //########################################################################
653 //# EXTENSION API
654 //########################################################################
658 #include "clear-n_.h"
662 /**
663  * API call to save document
664 */
665 void
666 PovOutput::save(Inkscape::Extension::Output */*mod*/,
667                         SPDocument *doc, gchar const *filename_utf8)
669     /* See comments in JavaFSOutput::save re the name `filename_utf8'. */
670     saveDocument(doc, filename_utf8);
675 /**
676  * Make sure that we are in the database
677  */
678 bool PovOutput::check (Inkscape::Extension::Extension */*module*/)
680     /* We don't need a Key
681     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
682         return FALSE;
683     */
685     return true;
690 /**
691  * This is the definition of PovRay output.  This function just
692  * calls the extension system with the memory allocated XML that
693  * describes the data.
694 */
695 void
696 PovOutput::init()
698     Inkscape::Extension::build_from_mem(
699         "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
700             "<name>" N_("PovRay Output") "</name>\n"
701             "<id>org.inkscape.output.pov</id>\n"
702             "<output>\n"
703                 "<extension>.pov</extension>\n"
704                 "<mimetype>text/x-povray-script</mimetype>\n"
705                 "<filetypename>" N_("PovRay (*.pov) (paths and shapes only)") "</filetypename>\n"
706                 "<filetypetooltip>" N_("PovRay Raytracer File") "</filetypetooltip>\n"
707             "</output>\n"
708         "</inkscape-extension>",
709         new PovOutput());
716 }  // namespace Internal
717 }  // namespace Extension
718 }  // namespace Inkscape
721 /*
722   Local Variables:
723   mode:c++
724   c-file-style:"stroustrup"
725   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
726   indent-tabs-mode:nil
727   fill-column:99
728   End:
729 */
730 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :