0
$\begingroup$

I want to create an animation from multiple 3D charts generated by ListPlot3D. Here is a code that generated "test" data for that (it will output frames into a folder "C:\EeInf\AnimationTest\", so it must exist):

nn = 100;
tt = 100;

InitialCondition[x_, y_] := If[x == 0 && y == 0, 10.0, 0];

(*Function to simulate one step of diffusion*)
Diffuse[data_] := 
  Module[{newData = data}, 
   Do[newData[[i, j]] = 
     Mean[{data[[Max[i - 1, 1], j]], 
       data[[Min[i + 1, Length[data]], j]], data[[i, Max[j - 1, 1]]], 
       data[[i, Min[j + 1, Length[data[[i]]]]]], data[[i, j]]}], {i, 
     Length[data]}, {j, Length[data[[i]]]}];
   newData];

xCoord = Table[x, {x, -1, 1, 2/nn}];
yCoord = Table[y, {y, 0, 25, 25/nn}];

Do[If[i == 1, 
  d = Table[
     InitialCondition[x, y], {x, -1, 1, 2/nn}, {y, 0, 25, 25/nn}];, 
  d = Diffuse[d];];
 Export["C:\\EeInf\\AnimationTest\\testData_" <> 
   IntegerString[i, 10, 6] <> ".m", 
  data = Flatten[
    Table[{xCoord[[ii]], yCoord[[jj]], d[[ii, jj]]}, {ii, 1, 
      Length[xCoord]}, {jj, 1, Length[yCoord]}], 1]];, {i, 1, tt}]

Print["Done."]

Here is the animation part:

filePrefix = "testData_"

CreateAnimation[prefix_, outputFormat_, resolution_, combinedFormat_, 
  duration_] := 
 Module[{files, frames, animation, frameData, startTime, endTime, 
   startMemory, endMemory, memoryUsed, timeTaken, maxPadding},
  maxPadding = 10;
  
  startTime = AbsoluteTime[];
  startMemory = MemoryInUse[];
  files = FileNames["C:\\EeInf\\AnimationTest\\" <> prefix <> "*.m"];
  files = Sort[files];
  
  Print["Loading ", Length[files], " files..."];
  
  frames = 
   Table[frameData = Import[fileName]; 
    Rasterize[
     ListPlot3D[frameData, ImageSize -> resolution, 
      PlotTheme -> {"Classic", "ClassicLights"}, 
      PlotRange -> All]], {fileName, files}];
  
  Print["Rasterized ", Length[frames], " frames."];
  
  If[combinedFormat =!= "", 
   animation = ListAnimate[frames, Length[frames]/duration];
   Export["C:\\EeInf\\AnimationTest\\animation." <> combinedFormat, 
    animation, "AnimationRepetitions" -> Infinity, 
    "ControlAppearance" -> None];];
  
  endTime = AbsoluteTime[];
  endMemory = MemoryInUse[];
  timeTaken = endTime - startTime;
  memoryUsed = endMemory - startMemory;
  Print["Time taken: ", timeTaken, " seconds"];
  Print["Memory used: ", memoryUsed, " bytes"];
  Print["Exported animation."];
  ]

CreateAnimation[filePrefix, "png", 200, "avi", 20]
Print["Done"]

It runs for a couple of minutes and creates a file animation.avi in the folder "C:\EeInf\AnimationTest\".

The problem is as the test data "evolve", max z value changes substantially (this is also expected for the real data). Because of that Mathematica adjusts the size of the bounding box, including size and position of the bottom size of that box. As a result, the animation looks jaggy because that bottom size constantly changes. I want it to stay the same so that the x, y, and z axis are always at the same place and have the same lengths (same as on the first chart). Subsequently, x and y axes labels should also stay at the same place and z axis labels should be determined automatically by Mathematica.

I can't figure out how to do that.

$\endgroup$
2
  • $\begingroup$ Instead of using PlotRange -> All; specify the PlotRange for the largest range used. $\endgroup$
    – Bob Hanlon
    Dec 6, 2023 at 15:35
  • $\begingroup$ @Bob-Hanlon, not that's not what I want. As written in the question, the z-range of the real data changes substantially. So if I set z-range to the max z used, then many charts will look like nearly zero. I explicitly want to have different z-range for each chart. The question is how to "fix" the bounding box size and position on the chart. $\endgroup$ Dec 6, 2023 at 17:04

1 Answer 1

0
$\begingroup$

The way to control the bounding box is through ImagePadding option of ListPlot3D (and, I guess, other 3D charts). By default it is set to Automatic, which means that Mathematica adjusts the bounding box to fit all the labels of all the axes (and so on). If many 3D charts are supposed to be combined into an animation and the box needs to be the same, the we need to find the smallest box (basically the chart with the longest labels on z axis) and then use the largest among all charts paddings to adjust all the charts. To extract ImagePadding out of a given chart AbsoluteOptions can be used. Here is an updated version of the animation code that does all that:

filePrefix = "testData_";
files = FileNames["C:\\EeInf\\AnimationTest\\" <> filePrefix <> "*.m"];

GetImagePadding[g_] := Module[{options, imagePadding},
   options = AbsoluteOptions[g, ImagePadding];
   imagePadding = ImagePadding /. options;
   If[imagePadding === Automatic || imagePadding === ImagePadding, {0,
      0}, {First[First[imagePadding]], First[Last[imagePadding]]}]];

CreateAnimation[prefix_, outputFormat_, resolution_, combinedFormat_, 
   duration_] := 
  Module[{files, charts, frames, animation, frameData, startTime, 
    endTime, startMemory, endMemory, memoryUsed, timeTaken, paddings, 
    xPadding, yPadding, noOfFiles},
   
   startTime = AbsoluteTime[];
   startMemory = MemoryInUse[];
   Print["Starting..."];
   files = FileNames["C:\\EeInf\\AnimationTest\\" <> prefix <> "*.m"];
   files = Sort[files];
   noOfFiles = Length[files];
   
   Print["Loading ", noOfFiles, " files..."];
   charts = Table[
     Print["Loading file: ", fileName, ". Memory used: ", 
      Round[(MemoryInUse[] - startMemory)/10^9, 0.001], 
      "GB, time taken: ", Round[AbsoluteTime[] - startTime], 
      " seconds."];
     frameData = Import[fileName];
     ListPlot3D[frameData, ImageSize -> resolution, 
      PlotTheme -> {"Classic", "ClassicLights"}, PlotRange -> All],
     {fileName, files}
     ];
   
   paddings = Table[GetImagePadding[charts[[i]]], {i, 1, noOfFiles}];
   xPadding = Max[Table[paddings[[i, 1]], {i, 1, noOfFiles}]];
   yPadding = Max[Table[paddings[[i, 2]], {i, 1, noOfFiles}]];
   charts = 0;
   
   Print["Rasterizing ", noOfFiles, " files..."];
   frames = Table[
     Print["Rasterizing file: ", fileName, ". Memory used: ", 
      Round[(MemoryInUse[] - startMemory)/10^9, 0.001], 
      "GB, time taken: ", Round[AbsoluteTime[] - startTime], 
      " seconds."];
     frameData = Import[fileName];
     Rasterize[
      ListPlot3D[frameData, ImageSize -> resolution, 
       PlotTheme -> {"Classic", "ClassicLights"}, PlotRange -> All, 
       ImagePadding -> {{xPadding, 0}, {yPadding, 0}}]],
     {fileName, files}
     ];
   
   Print["Rasterized ", Length[frames], " frames."];
   
   If[combinedFormat =!= "", 
    animation = ListAnimate[frames, Length[frames]/duration];
    Export["C:\\EeInf\\AnimationTest\\animation." <> combinedFormat, 
     animation, "AnimationRepetitions" -> Infinity, 
     "ControlAppearance" -> None];];
   
   endTime = AbsoluteTime[];
   endMemory = MemoryInUse[];
   timeTaken = Round[endTime - startTime];
   memoryUsed = Round[(endMemory - startMemory)/10^9, 0.001];
   Print["Time taken: ", timeTaken, " seconds."];
   Print["Memory used: ", memoryUsed, " GB."];
   Print["Exported animation."];
   ];

CreateAnimation[filePrefix, "png", Large, "avi", 50];
Print["Done"];

It took a good while to figure this out.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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