Hi,
I accidentally uncovered what appears to be a parsing bug within Manipulate. The documented syntax is:
Manipulate[expr, ...]
where expr can be one or a compound expression, e.g.:
Manipulate[exprA, ...]
OR
Manipulate[exprA;exprB, ...] (* note the semicolon separating the individual expressions *)
But I discovered that things (*seemingly*) work just fine, along with some bonus behavior (more below), for:
Manipulate[Dynamic[exprA] exprB, ...]
but in the above, the first argument is not a compound expression at all! I'm surprised it even parsed successfully!
However, if one uses proper syntax, the bonus behavior is disabled.
The bonus behavior is that exprA within Dynamic[exprA ] can be a set of clauses (e.g. a compound expression)
that impose constraints on the control variables or do other useful things in response
to changes in TrackedSymbols, *without* triggering the main Manipulate re-eval loop. Evidently, the parsing bug includes the result of
Dynamic as part of the displayable output by the Front End.
Is this a bug or expected behavior? In either case, I like it :-)
Here is a code example (2 input cells), and the comments therein describe the bonus behavior:
genDemo[] := Manipulate[
(* Place controller value constraints here, wrapped in Dynamic[] to preclude re-eval loop that recomputes the display graphic. However,
to use this idiom, make the last statement within the Dynamic an invisible displayable output such as Spacer[0], NOT followed by ;.
This precludes Null showing up in the display output. ALSO! Do NOT terminate the Dynamic[] with a semicolon,
or this mechanism won't operate! Why this works symtactically at all is a mystery! *)
Dynamic[
If[Ceiling[cnx] < Ceiling[cty], cty = Ceiling[cnx]];
If[Ceiling[cnx] < Ceiling[ctx], ctx = Ceiling[cnx]];
Spacer[0] (* TRY DELETING THIS STATEMENT. If you do, then Null appears in output display. *)
] (* TRY PLACING A SEMICOLON HERE. If you do, then these control constraints won't be operable *)
(* The below With[] idiom prevents makeRGBImg from being reinvoked each time the target location changes [which
should merely update the red dot location in display] *)
GraphicsGrid[{{With[{img = Table[RandomReal[], {cnx}, {cnx}]},
Dynamic[Image[makeRGBImg[img, cty, ctx]]]]}}, ImageSize -> 150],
(* The last executable statement above must terminate with a comma, thus marking the division between
the "action" and control layout specification sections of the Manipulate[] *)
{{cnx, vnx, Tooltip[lnx, tnx]}, ControlType -> InputField},
{{ctx, vtx, Tooltip[ltx, ttx]}, 1, cnx, Appearance -> "Labeled"},
{{cty, vty, Tooltip[lty, tty]}, 1, cnx, Appearance -> "Labeled"},
LocalizeVariables -> True,
ControlPlacement -> Left,
TrackedSymbols :> {cnx, ctx, cty}, ContinuousAction -> False,
SynchronousUpdating -> False,
Initialization :> {tnx = "Number of spatial samples (square image)",
lnx = "Number of Pixels Width",
vnx = 100,
ttx = "Target X pixel ",
ltx = "Target X Pixel",
vtx = 65.,
tty = "Target Y pixel",
lty = "Target Y Pixel",
vty = 47.9 ,
makeRGBImg[img_, cty_, ctx_] := Module[{rgbimg},
rgbimg = Map[{#, #, #} &, img, {2}];
rgbimg[[Ceiling[cty], Ceiling[ctx]]] = {1., 0, 0}; (* Insert red target marker *)
Return[rgbimg]]
} (* end of Initialization *)
]
(Text Cell) Try reducing the 1st control's value (# pixels width) to less than the target pixel location index.
If control constraints wrapped inside Dynamic are operable, then the target pixel location controls
should update without being left "out-of-bounds" red.
genDemo[]