Arrays
How to update
You might notice if you try to offload an array of points (aka List
)
pts = RandomReal[{-1,1}, {100, 2}];
Graphics[Point[pts // Offload], TransitionDuration->1000]
and then try to perform operations per element
Do[pts[[i]] += 0.1 Normalize[{-1,1} pts[[i, {2,1}]]], {i, 100}];
Nothing will happen
Per-element operations on List
do not trigger a data synchronization between Wolfram Kernel and frontend. The only way to trigger it for lists is
pts = pts;
This approach will definitely work, however, for such example it is easier and more efficient to do the same using a pure function and Map
pts = Map[(# + 0.1 Normalize[{-1,1} #[[{2,1}]]])&, pts];
Per-element customization
In the example above we tried the ideal spherical case when one can substitute pts
into a single primitive Point
. In reality you might need to make then to have different properties, colors and etc. For this we need to address each element by the index explicitly and wrap everything into Offload.
For example not like that ❌
pts = RandomReal[{-1,1}, {100, 2}];
Graphics[
Table[{Point[pts[[i]]]}, {i, 100}] // Offload,
TransitionDuration->1000
]
but like this ✅
pts = RandomReal[{-1,1}, {100, 2}];
Graphics[
Table[With[{i=i}, {Point[pts[[i]]] // Offload}], {i, 100}],
TransitionDuration->1000
]
Details
Another option
This is also validpts = RandomReal[{-1,1}, {100, 2}];
Graphics[
Table[With[{i=i}, {Point[pts[[i]] // Offload]}], {i, 100}],
TransitionDuration->1000
]
We don't dynamically create points, we statically create them on Wolfram Kernel and offload the position of each point to be evaluated (dynamically) on the frontend.
Let's stylize our points
pts = RandomReal[{-1,1}, {400, 2}];
col[r_] := With[{n=Clip[Norm[r]/2]}, RGBColor[1-n, n, n]]
Graphics[
Table[With[{i=i}, {col[pts[[i]]], Point[pts[[i]]] // Offload}], {i, 400}],
TransitionDuration->1000
]
Do[
pts = Map[(# + 0.1 Normalize[{-1,1} #[[{2,1}]]])&, pts];
Pause[0.3];
, {50}];
With zero-cost we can generate more points using the same pts
using simple math operations (will be executed on the frontend dynamically)
pts = RandomReal[{-1,1}, {400, 2}];
col[r_] := With[{n=Clip[Norm[r]/2]}, RGBColor[1-n, n, n]]
Graphics[
Table[With[{i=i}, {col[pts[[i]]], Point[1.1 pts[[i]]] // Offload, Point[pts[[i]]] // Offload}], {i, 400}],
TransitionDuration->1000
]
Multidimensional arrays
The idea is the same, however, you have to be careful with Part function. WLJS Interpreter does not fully support all crazy syntax of Wolfram Language.
Instead of ❌
multipts[[i, j]] // Offload
do this ✅
multipts[[i]][[j]] // Offload
On Wolfram Kernel it is considered to be less efficient, but our Javascript implementation does the second Part
using references only.
Here is an example with a multidimensional, where the first part stores color and the second one stores the position
mpts = {Clip[Norm[#]], #} &/@ RandomReal[{-1,1}, {400, 2}];
Graphics[
Table[With[{i=i}, {
Hue[mpts[[i]][[1]]],
Point[mpts[[i]][[2]]]
} // Offload], {i, 400}],
TransitionDuration->30
]
Do[
mpts = Map[{
If[#[[1]]>0.95, 0, #[[1]] + 0.025],
#[[2]] + 0.01 Normalize[{-1,1} #[[2, {2,1}]]]
}&, mpts];
Pause[1/30.0];
, {100}];
For smoother animations consider to use AnimationFrameListener. See how in Animation