5
$\begingroup$

I'm trying to track the movement of a bunch of particles by adding fading trail effect and dynamically update particle's position. However, when I need to track several hundreds of them, drawing may take too much time, making dynamic updating impossible. So I would like to ask are there any way to create an effective fading trail effect in Mathematica, maybe proper tuning of Dynamic or drawing method may help.

The effect I would like to achieve is as follow:

(*Test data*)
priml = Table[# + {.07 k, Sin[.15 k]} & /@ Tuples[Range@15, 2], {k, 
    130}];
cf = Blend[{Yellow, Blend[{Orange, Red}]}, #/1.5] &;
coll = cf /@ Range[0., 1.5, 0.05];
sizel = .15 Range[0., 1.5, .05]^1.5;

(*Update position*)
Do[
  FinishDynamic[];
  track = priml[[i ;; i + 30]];
  , {i, 100}] // AbsoluteTiming

(*Dynamically create the fading trail effect.*)
Dynamic@Graphics[
  Thread[{coll, 
    MapThread[
     Function[{dat, size}, Disk[#, size] & /@ dat], {track, sizel}]}],
   PlotRange -> {{-2, 25}, {-2, 17}}]

Sample effect

Well, but for simplicity, the first step could be setting sizel to a constant array ConstantArray[.15,31].

Actually the only change between two consecutive frames is the addition of new head, deletion of the tail, and the alternation in color, so I strongly suspect lot of time could be saved by proper updation instead of complete redraw the graphics.

In my real application, the calculation in a single Do loop takes about 0.5s, so a drawing time less than 0.05s would be satisfying.

Any idea?

Note: Slight decrease in quality is accepted, e.g. Raster method is allowed

$\endgroup$
  • $\begingroup$ 4847 $\endgroup$ – Kuba Apr 7 '17 at 5:51
  • $\begingroup$ sounds like a job for GPU/CUDA or at least for a Compile[expr,CompilationTarget->"C",Parallelization->True,RuntimeOptions->Listable] which will essentially add all of your trails in parallel over the entire list of your particles using machine-compiled C code. $\endgroup$ – Gregory Klopper Apr 7 '17 at 6:01
  • 1
    $\begingroup$ Is the florid effect necessary? If you just need a fading trail, the implementation can be much easier and faster. $\endgroup$ – xzczd Apr 7 '17 at 6:50
  • $\begingroup$ @xzczd yep,the color is necessary. But I suppose this effect could be easily achieved by ColorCombine. As the color function is just a linear blend so it won't be that hard. $\endgroup$ – Wjx Apr 7 '17 at 8:21
  • 1
    $\begingroup$ For multiple copies of the same object it is often faster to use Translate. For example use MapThread[{#1, Translate[Disk[{0, 0}, #2], #3]} &, {coll, sizel, track}] as your Graphics expression. $\endgroup$ – Simon Woods Apr 7 '17 at 19:02
2
$\begingroup$

Errr, I found one partial solution with the help by @njpipeorgan, use raster image instead of vector image and use listable operations to update frames to ensure high efficiency.

(*Set plot range and length resolution*)
{{xmin, xmax}, {ymin, ymax}} = {{-2, 25}, {-2, 17}};
resolution = .05;

(*simple convertion*)
convert[{x_, y_}] := Round[{x - xmin, y - ymin}/resolution];
convert[s_] := Round[s/resolution]

(*Create a frame, a "frame" is given by a raster array here*)
createmat[cen_, r_] := 
 Block[{arr = ConstantArray[0, convert[{xmax, ymax}]], pr = convert@r},
  ((arr[[#1 - pr ;; #1 + pr, #2 - pr ;; #2 + pr]] = 
         1.06 DiskMatrix@pr) & @@ convert@#) & /@ cen;
  arr]

(*Update the current image, add new frame and make modification to older frames so that it looks like a fading trail: 1. color alternation 2. Shrinking in size*)
update[mat_, newprim_, r_] := 
 Clip[mat + 
   createmat[newprim, 
    r] - .05 UnitStep[GradientFilter[mat, 1] - .2] - .06, {0, 1}]

(*Define frames and color used in rendering*)
priml = Table[# + {.07 k, Sin[.15 k]} & /@ Tuples[Range@15, 2], {k, 
    130}];
col1 = List @@ Yellow;
col2 = List @@ Blend[{Orange, Red}];

(*This function will convert a numeric 2-D array to a colored image using linear blend*)
restorecol[mat_, col1_, col2_] := 
 ColorCombine[
  MapThread[
   Image[1 + Unitize[mat]*(mat #2 + (1 - mat) #1 - 1)] &, {col1, 
    col2}], "RGB"]

(*Dynamic!*)
Dynamic@restorecol[Reverse@Transpose@mat, col1, col2]

(*Initialization*)
mat = createmat[{}, .15];

(*Update*)
Do[
 mat = update[mat, priml[[i]], .15],
 {i, 100}]

A so-so result

The performance is satisfying on my computer, update a frame cost 0.06s in total.

But the usage of Raster is, in some ways, accelerating the process at a cost of decreased quality, so I still hope there could be a better answer!

Also, the performance still can be further improved, so any suggestions on this direction is also welcomed!

$\endgroup$

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.