# Fast DensityPlot

Is it possible to increase the performance of the DensityPlot?

For example, let's try to plot the following "flower"

f[x_, y_] := (x^2 + y^2) Exp[-x^2 - y^2] Sin[10 Sqrt[x^2 + y^2] + 10 ArcTan[x, y]]^4;

DensityPlot[f[x, y], {x, -3, 3}, {y, -3, 3}, PlotPoints -> 200,
MaxRecursion -> 3, ColorFunction -> Hue, PlotRange -> All,
ColorFunctionScaling -> False, ImageSize -> 600] This toy example takes about 12 seconds on my laptop, eats about 1GB of RAM while plotting, and the 34MB result slows down the notebook.

The questions are:

1. How to increase the speed?

2. How to decrease the MaxMemoryUsed?

3. How to decrease the size of the output?

Edit: tested with MMA 11.1, option Exclusions -> None added to recover the previous behavior.

I have found that my approach with textures has different applications:

Now I want to use it for the enhancement of the DensityPlot:

Options[fastDensityPlot] = Append[Options[DensityPlot], Subpoints -> 30];
SyntaxInformation[fastDensityPlot] = SyntaxInformation[DensityPlot];

fastDensityPlot[f_, {x_, xmin_, xmax_}, {y_, ymin_, ymax_}, opts : OptionsPattern[]] :=
DensityPlot[f, {x, xmin, xmax}, {y, ymin, ymax},
Evaluate@FilterRules[{opts}, Except@Subpoints]] // Normal // toTriangles //
texturize[Function[{#1, #2}, #3] & @@ {x, y, f},
OptionValue[Subpoints], OptionValue[ColorFunction]]


Here Normal converts GraphicsComplex to separate polygons, toTriangles splits polygons to triangles, and texturize puts textures on every triangle (defined below), f is assumed to be Listable.

f[x_, y_] := (x^2 + y^2) Exp[-x^2 - y^2] Sin[10 Sqrt[x^2 + y^2] + 10 ArcTan[x, y]]^4;

fastDensityPlot[f[x, y], {x, -3, 3}, {y, -3, 3}, PlotPoints -> 10,
MaxRecursion -> 2, ColorFunction -> Hue, Subpoints -> 20,
PlotRange -> All, ImageSize -> 600, Exclusions -> None] This image looks a bit better. At the same time fastDensityPlot is ~10 times faster then the regular DensityPlot, MaxMemoryUsed is only 64MB and ByteCount is 10MB.

One can see that fastDensityPlot uses the advantage of the non-equidistant mesh:

fastDensityPlot[f[x, y], {x, -3, 3}, {y, -3, 3}, PlotPoints -> 10,
MaxRecursion -> 2, ColorFunction -> Hue, Subpoints -> 20,
PlotRange -> All, ImageSize -> 600, Mesh -> All, Exclusions -> None] Definitions of the above functions:

toTriangles = # /. Polygon[v_ /; Length[v] > 3, ___] :> (Polygon@Append[#, Mean[v]] & /@
Partition[v, 2, 1, 1]) &;

texturize[f_, n_, colf_] := # /. Polygon[{v1_, v2_, v3_}, ___] :> {Texture@
ImageData@Colorize[
Image@f[v3[] + (v1[] - v3[]) #1 + (v2[] - v3[]) #2,
v3[] + (v1[] - v3[]) #1 + (v2[] - v3[]) #2]
&[#, Transpose[#]] &@ConstantArray[Range[-1./n, 1 + 1./n, 1./n], n + 3],
ColorFunction -> colf, ColorFunctionScaling -> False],
Polygon[{v1, v2, v3},
VertexTextureCoordinates -> {{1 - 1.5/(n + 3),
1 - 1.5/(n + 3)}, {1.5/(n + 3), 1.5/(n + 3)}, {1.5/(n + 3),
1 - 1.5/(n + 3)}}]} &;


As in the linked answer, I add textures to every triangle with an appropriate rectangular grid. This method is fast because it uses packed arrays.

• As I understand, you subdivide each triangle into Subpoints^2 regions and calculate values of f in the nodes. Is it correct? – Alexey Popkov Jan 5 '14 at 14:24
• Yes! More precisely, I calculate the texture for a parallelogram and use only a half of it. One can optimize it further, but it is no so simple. – ybeltukov Jan 5 '14 at 14:30
• So PlotPoints->n, Subpoints->m, MaxRecursion->0 should be equivalent to PlotPoints->n*m, Subpoints->0, MaxRecursion->0? – Alexey Popkov Jan 5 '14 at 14:45
• @AlexeyPopkov Yes, but with Subpoints->1 in the second case. – ybeltukov Jan 5 '14 at 15:03
• @AlexeyPopkov They are visually similar, but not equivalent. The second one contains a lot of small triangles with a lot of small textures. This is very slow and inefficient. fastDensityPlot is fast when you use a small number of big triangles with big textures (two big triangles in the limit). The opposite limit is DensityPlot which produce a lot of small colorized (not texturized) triangles. – ybeltukov Jan 5 '14 at 15:52