In principle, this all is not difficult but there are some obstacles in the way that will make life hard:
- 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.
- 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
- 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.
- 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:
- Create your 3D graphics. Add your custom primitives for axes etc.
- Choose a projection or extract the projection parameters from a 3D Mathematica graphics. This gives you a projection matrix in homogeneous coordinates
- Project all primitives
- 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.
- Turn all polygons with vertex colors to uniformly colored polygons
- 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]
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]}
]
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.
{}
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$FullForm
to inspect the code for the plot. RemoveGraphicsComplex
using something likeplot /. gc : GraphicsComplex[___] :> Normal[gc]
. You will now have graphics primitives with lists of three-dimensional coordinates. Project the coordinates along the vector betweenViewCenter
andViewPoint
. You can now reduce all the three-dimensional coordinates to two-dimensional coordinates, and visualize this withGraphics
(as opposed toGraphics3D
). When you export the 2D plot as SVG, it will be vectorized as desired. $\endgroup$