Here is a refactor of Kuba's wonderful answer. I hope it may help somebody understand the order in which things are evaluated better. This version should also be resistant against conflicting symbol names, though perhaps it would have been easier to achieve that using contexts. A few things that I thought might be unnecessary have been removed.
n = 100;
names = Range[n];Permute[Range[10*n], RandomPermutation[10*n]][[;; n]];
pts = AssociationThread[names -> N@CirclePoints[n]];
edgesedgesIndices = RandomSample[Subsets[names
RandomSample[Subsets[Range[n], {2}], Quotient[n Log[n], 2]];
edges = Map[names[[#]] &, edgesIndices, {2}];
heldStates =
Join @@ (ToExpression["state" <> ToString[#] , InputForm, Hold] & /@
names);
dynModVars = List @@@ Hold@Evaluate[Set @@@ Thread[{
heldStates,
Hold @@ ConstantArray[False, n]
}, Hold]];
preMapThread = Apply[List,
Hold@Evaluate[
Join[heldStates[[#]] & /@ Transpose@edgesTranspose@edgesIndices, Transpose@edges]],
{1, 2}];
preAppMap = Thread[{heldStates, Hold @@ names}, Hold];
edgeDisplayerMaker = Function[
{sA, sB, name1, name2},
{DynamicBox[
If[FEPrivate`Or[sA, sB], RGBColor[1, 0, 1], RGBColor[0, 1, 0]]],
Line[{pts[name1], pts[name2]}]}
, HoldAll];
interactivePointMaker = Function[
{sA, name},
{DynamicBox[If[sA, RGBColor[1, 0, 1], RGBColor[0, 1, 0]]],
EventHandler[
Point@pts[name], {"MouseEntered" :> FEPrivate`Set[sA, True],
"MouseExited" :> FEPrivate`Set[sA, False]}]}, HoldAll];
Perhaps the structure of the DynamicModule
is now a little clearer.
DynamicModule @@ {
Unevaluated @@ dynModVars
,
Unevaluated@
Graphics[{
MapThread @@ {
edgeDisplayerMaker,
Unevaluated @@ preMapThread},
PointSize[0.025],
List @@ interactivePointMaker @@@ preAppMap
}, ImageSize -> Large]}