Skip to main content
Adapted code so that it works for arbitrary names
Source Link
Jacob Akkerboom
  • 12.2k
  • 46
  • 82

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]}

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];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], Quotient[n Log[n], 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@edges, 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]}

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 = Permute[Range[10*n], RandomPermutation[10*n]][[;; n]];
pts = AssociationThread[names -> N@CirclePoints[n]];
edgesIndices = 
  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@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]}
Removed SameQ
Source Link
Jacob Akkerboom
  • 12.2k
  • 46
  • 82

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];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], Quotient[n Log[n], 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@edges, Transpose@edges]],
   {1, 2}];
preAppMap = Thread[{heldStates, Hold @@ names}, Hold];
edgeDisplayerMaker = Function[
   {sA, sB, name1, name2},
   {DynamicBox[
     If[SameQ[FEPrivate`Or[sAIf[FEPrivate`Or[sA, sB], True], RGBColor[1, 0, 1], 
      RGBColor[0, 1, 0]]], 
    Line[{pts[name1], pts[name2]}]}
   , HoldAll];
interactivePointMaker = Function[
   {sA, name},
   {DynamicBox[
     If[SameQ[sA, True]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]}

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];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], Quotient[n Log[n], 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@edges, Transpose@edges]],
   {1, 2}];
preAppMap = Thread[{heldStates, Hold @@ names}, Hold];
edgeDisplayerMaker = Function[
   {sA, sB, name1, name2},
   {DynamicBox[
     If[SameQ[FEPrivate`Or[sA, sB], True], RGBColor[1, 0, 1], 
      RGBColor[0, 1, 0]]], Line[{pts[name1], pts[name2]}]}
   , HoldAll];
interactivePointMaker = Function[
   {sA, name},
   {DynamicBox[
     If[SameQ[sA, True], 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]}

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];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], Quotient[n Log[n], 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@edges, 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]}
Source Link
Jacob Akkerboom
  • 12.2k
  • 46
  • 82

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];
pts = AssociationThread[names -> N@CirclePoints[n]];
edges = RandomSample[Subsets[names, {2}], Quotient[n Log[n], 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@edges, Transpose@edges]],
   {1, 2}];
preAppMap = Thread[{heldStates, Hold @@ names}, Hold];
edgeDisplayerMaker = Function[
   {sA, sB, name1, name2},
   {DynamicBox[
     If[SameQ[FEPrivate`Or[sA, sB], True], RGBColor[1, 0, 1], 
      RGBColor[0, 1, 0]]], Line[{pts[name1], pts[name2]}]}
   , HoldAll];
interactivePointMaker = Function[
   {sA, name},
   {DynamicBox[
     If[SameQ[sA, True], 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]}
Post Made Community Wiki by Jacob Akkerboom