6
$\begingroup$

For 3D, Mathematica does not export SVG as vector graphics, it just puts an encoded png image inside svg file. Same happens if one exports as .eps or .pdf

This question does not address the problem at all, as the method pointed out still produces embedded rastter image in the different file formats.

Export Plot3D in Mathematica 10.1 is Rasterized by default

I have found a solution, that involves exporting the 3D plot as a 3D Autodesk file, such as .3ds or .wrl

This yields a 3d file which contains the 3d plot where you can rotate.

Now my objective is to export this 3D object as a 2D svg (scalable vector) file, which involves exporting a 2D angle of view of the 3D object itself.

If one puts the .wrl or .3ds file into any viewer (Autodesk, Blender, 3D Builder) it will show differently from mathematica, ie. no axis grid, and very different scaling.

Rasterized exports: export as .eps, .svg, .pdf

Right click Print to pdf also yields raster, but even worse, with image compression.

How can one do this?

Link to my files:

https://www.dropbox.com/sh/lg2j1ib5ap5s7cu/AAD9_xniH1Vuwu_QwlM8OL12a?dl=0

screenshot

$\endgroup$
  • 3
    $\begingroup$ Post code, not pictures of code. Edit your post and paste in the code. Then select it and use the {} button to format the block of code. Notice that the questions on this site do not post pictures of code as very few will spend their time typing out your code to help you. $\endgroup$ – Edmund Nov 8 '17 at 2:11
  • $\begingroup$ @Edmund alright, sorry and thanks for pointing out $\endgroup$ – Lagrangian Nov 8 '17 at 2:13
  • $\begingroup$ This has come up in the chat room before. Apparently it's a very difficult problem, because while we can project the polygons in the 3D figure onto a 2D surface, the resulting SVG file will be much larger than it should be, since it will include polygons which are hidden by other polygons on top of it. How to remove these polygons efficiently is a difficult problem, as I understand it. $\endgroup$ – C. E. Nov 8 '17 at 4:39
  • 2
    $\begingroup$ FWIW, the outline of the approach that one would take is this: Use FullForm to inspect the code for the plot. Remove GraphicsComplex using something like plot /. gc : GraphicsComplex[___] :> Normal[gc]. You will now have graphics primitives with lists of three-dimensional coordinates. Project the coordinates along the vector between ViewCenter and ViewPoint. You can now reduce all the three-dimensional coordinates to two-dimensional coordinates, and visualize this with Graphics (as opposed to Graphics3D). When you export the 2D plot as SVG, it will be vectorized as desired. $\endgroup$ – C. E. Nov 8 '17 at 18:10
  • 2
    $\begingroup$ @Lagrangian Here is an example of what can be done. $\endgroup$ – C. E. Nov 8 '17 at 18:13
8
$\begingroup$

In principle, this all is not difficult but there are some obstacles in the way that will make life hard:

  1. In a 2d projection of a 3d polygon graphics, many of the polygons are not visible since they are optically behind others. In the general case, it is at least a partially complex task to remove those that are completely hidden. If you leave all polygons and just paint over the ones that are in the background (like it was done in PDF exported graphs in older versions of Mathematica), you will end up with very large file sizes and take ages to render in a viewer.
  2. SVG does not support polygons that have different colors for each vertex and use interpolation for a smooth transition. This has a greater effect as one might anticipate first. Color interpolation for polygons really make most of the smooth surface-look
  3. Mathematica does not export polygons to SVG primitives if they use VertexColors as described in 2. All Mathematica graphics, on the other hand, will use this automatically and to my knowledge, there is no simple switch to turn it off. You need to transform the polygons yourself.
  4. Wolfram made it almost impossible to extract graphics primitives for axes, ticks, frames, etc. that are created automatically. When you project a 3D plot to 2d by converting polygons and lines, you will need a custom way to add axes or probably spend time debugging the current framework to reuse internal functions

The main approach, however, is somewhat simple:

  1. Create your 3D graphics. Add your custom primitives for axes etc.
  2. Choose a projection or extract the projection parameters from a 3D Mathematica graphics. This gives you a projection matrix in homogeneous coordinates
  3. Project all primitives
  4. Apply the algorithm from 1. above or sort the graphics primitives by the distance from the camera. Things far away need to be drawn first of course.
  5. Turn all polygons with vertex colors to uniformly colored polygons
  6. Export the projected graphics to SVG

Here is a small example that skips steps 1-3 and uses 2d polygons from the start:

f[n_, x_] := Sqrt[2] Sin[n*Pi*x];
s[n_, m_] := 
  Function[{x, y}, (f[n, x] f[m, y] + f[n, y] f[m, x])/Sqrt[2]];
dens = Normal@
  DensityPlot[-s[3, 1][x, y], {x, 0, 1}, {y, 0, 1}, 
   ColorFunction -> "AvocadoColors", Frame -> False, PlotPoints -> 10,
    MeshFunctions -> {#3 &}, MeshStyle -> Directive[Thickness[.002]], 
   Mesh -> 10]

Mathematica graphics

We skip step 4. since our polygons are all in one layer. Here is step 5, where I'm using the mean color of all vertices as replacement color for the whole polygon. Coloring the edges is important to get rid of visible spaces between the polygons. Maybe setting the thickness of the polygon edges to zero will work as well.

dens /. Polygon[pts_, VertexColors -> cols_] :> 
  With[{color = RGBColor @@ Mean[cols]},
   {EdgeForm[color], color, Polygon[pts]}
  ]

Mathematica graphics

This can now successfully be exported to SVG using

Export["~/tmp/dens.svg", %]

The file has already a size of about 2MB. If you want the surface indeed smooth, then you will need approximately 100 plot-points. That gives you a file with a size of 20MB.

$\endgroup$
  • 5
    $\begingroup$ "Wolfram made it almost impossible to extract graphics primitives for axes, ticks, frames, etc. that are created automatically." - that FullGraphics[] remains broken to this day is very annoying indeed. $\endgroup$ – J. M. is away Nov 10 '17 at 4:09
  • $\begingroup$ By the way why can't I set a modified color function ColorFunction -> (ColorData["AvocadoColors"][(-s[1, 3][#1, #2] + 2.5)/6] &) when in DensityPlot? This modification makes the low valued regions at the corners less black. $\endgroup$ – Lagrangian Nov 10 '17 at 9:12
  • 1
    $\begingroup$ First, in a DensityPlot the function you provide is directly the color-value. It doesn't make sense to apply a second color function. Look up the details section in the docs for ColorFunction and you see that colorfunctions for DensityPlot don't get x and y as parameters. You can only apply an additional function to the density value. That being said: Put your changed function directly as first parameter to DensityPlot and look up the option ColorFunctionScaling which is always True automatically. $\endgroup$ – halirutan Nov 11 '17 at 0:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.