# 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[[1]] + (v1[[1]] - v3[[1]]) #1 + (v2[[1]] - v3[[1]]) #2,
v3[[2]] + (v1[[2]] - v3[[2]]) #1 + (v2[[2]] - v3[[2]]) #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? Jan 5, 2014 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. Jan 5, 2014 at 14:30
• So PlotPoints->n, Subpoints->m, MaxRecursion->0 should be equivalent to PlotPoints->n*m, Subpoints->0, MaxRecursion->0? Jan 5, 2014 at 14:45
• @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. Jan 5, 2014 at 15:52
• To my embarrassment the problem was localized to the region between the keyboard and the chair. The definitions of toTriangles and texturize were being loaded from (38688) rather than this answer. Dec 22, 2019 at 1:16