Background
I'm working on an application in which I need to create and control two sets of locators. I know from reading the Mathematica documentation and certain posts on Mathematica.SE that this means I can't base my application on a Manipulate
expression. I have very little experience with interactive applications based on the dynamic objects that sit below Manipulate
, so I searched "multiple locators" to see what I could learn. Quite a bit as it turned out. However, to keep this short, I'll just say I finally settled on a approach described by jVincent in his answer to this question.
Question
In my situation I am going to have one set of locators that are displayed as black squares and a second set displayed as red dots. The application will start out by showing no locators in its content pane. The user will create as many of each kind as he/she wants by clicking on buttons provided for the purpose.
My adaptation of jVincent's code to my situation is as follows:
DynamicModule[{black = {}, red = {}},
Grid[{{
Framed@Graphics[
{Dynamic@MapIndexed[
With[{i = #2[[1]]},
Locator[Dynamic[black[[i]]],
Style[■, Black]]]&, black],
Dynamic@MapIndexed[
With[{i = #2[[1]]},
Locator[Dynamic[red[[i]]],
Style[●, Red]]] &, red]},
PlotRange -> {{0, 1}, {0, 1}}],
SpanFromLeft},
{Button["Add Black", AppendTo[black, RandomReal[1, 2]]],
Button["Add Red", AppendTo[red, RandomReal[1, 2]]]}}]]
The code works well. I have no complaints. One expression in the code surprises and intrigues me, however. This is:
{Dynamic@MapIndexed[
With[{i = #2[[1]]},
Locator[Dynamic[black[[i]]],
Style[■, Black]]]&, black]
It's clear what this does: it creates a list of locators based on a list of locations (pairs of real numbers). It's clear that the With
trick is needed to work around the HoldFirst
attribute of Dynamic
. What is clever and surprising -- I would have never thought of it -- is the use of MapIndexed
to obtain the indexes that must be inserted into black[[...]]
. What I would have thought of is:
{Dynamic@(With[{i = #},
Locator[Dynamic[black[[i]]], Style[■, Black]]] &
/@ Range@Length@black)
which is much more banal but gets the job done. I wonder why jVincent chose MapIndexed
? Is it really that more efficient than mapping over a Range
? Or is there some other deeper advantage beyond my ability to fathom.
I will say I don't think creating a range every time the expression in question is evaluated is much more expensive than what MapIndexed
does to create the pairs it uses, but I could be wrong. I'm afraid I'm one those Mathematica programmers who knows the value of everything but the cost of nothing (remembering an old joke made about Lisp programers).
foo[bar[[#]], {#}]& /@ Range@Length@bar
when you can simply writeMapIndexed[foo, bar]
" :) $\endgroup$