Glühender gewichteter Graph (Netzwerk): Eckpunkte und Kanten

Aug 19 2020

Ich versuche einen Weg zu finden (wünschenswert einfach und Leistung / Geschwindigkeit für größere Diagramme optimiert), um Folgendes zu tun:

  • Styling Graph Scheitelpunkte durch Glow-Effekt und seine Intensität abhängig von VertexWeight

  • Styling von Grafikkanten durch Glow-Effekt und deren Intensität abhängig von EdgeWeight

  • DirectedEdgeGlow-Effekt-Styling ist ebenfalls wünschenswert (während der Einfachheit halber Dinge beginnen können UndirectedEdge)

Zum Beispiel für so etwas:

RandomGraph[{20,100},
VertexWeight->RandomReal[1,20],
EdgeWeight->RandomReal[1,100],
Background->Black,
BaseStyle->White]

Ich suche nach einem ähnlichen Bild wie dem folgenden, außer dass auch die Kanten leuchten müssen:

Die Probleme, die ich erlebe.

1. Einfache Implementierung eines atemberaubenden Lichts

Ich habe verschiedene Glüheffekte gesehen (einschließlich DIESES über Glühpunkte), aber keinen Experten für die besten visuellen und Leistungsideen. Überraschenderweise habe ich auch nicht viel über leuchtende Linien gesehen. Ich würde naiv mit so etwas beginnen, aber das kann wahrscheinlich optisch und leistungsmäßig verbessert werden:

bsc=BSplineCurve[{{0,0},{1,1},{2,0}}];
Graphics[
    Table[{White,Opacity[1/k^1.2],Thickness[.005k],CapForm["Round"],bsc},{k,20}],
Background->Black]

2. Gewichte zum Leuchten bringen

Obwohl ich mir bewusst bin VertexShapeFunctionund EdgeShapeFunctionbin, bin ich mir nicht ganz sicher, wie ich die Gewichte optimal an sie weitergeben soll ... und ob diese Eigenschaften der richtige Ansatz sind.

Eingebaute Funktionen leuchten

Ich habe festgestellt, dass diese Funktionen etwas Leuchten erzeugen:

ComplexPlot[z^2+1,{z,-2-2I,2+2I},ColorFunction->"CyclicReImLogAbs"]

Und wie @EC in seiner Antwort unten bemerkt hat

ImageAdjust[DistanceTransform[Graphics[Point[RandomReal[1,{100,2}]]]]]

Vielen Dank, Ihre Hilfe wird sehr geschätzt!

Antworten

14 flinty Aug 19 2020 at 22:41

Sie können einen allgemeinen Glüheffekt erzielen, indem Sie ImageAddeine unscharfe Kopie der Bildmaske verwenden. Zugegeben, es ist ein bisschen einfach, aber der Effekt ist überzeugend. Ich entschied mich dafür, ein "Gehirn" -Netzwerk zu erstellen AnatomyDataund NearestNeighbourGraphes wie eine überhypte KI-Marketing-Sache aussehen zu lassen:

SeedRandom[123];
brain = AnatomyData[Entity["AnatomicalStructure", "Brain"], "MeshRegion"];
boundary = RegionBoundary[brain];
nng = NearestNeighborGraph[RandomPoint[boundary, 1000], 7];
brainnetimg = Rasterize[
   GraphPlot3D[nng, ViewPoint -> Left, 
    VertexStyle -> Directive[AbsolutePointSize[7], White], 
    EdgeStyle -> Directive[AbsoluteThickness[2], White], 
    Background -> Black]
   , ImageSize -> 1000];
ImageAdd[ImageAdjust[Blur[Binarize@brainnetimg, 7], .1], 
 ImageMultiply[brainnetimg, 
  LinearGradientImage[{Blue, Cyan, Purple}, 
   ImageDimensions[brainnetimg]]]]

Um die Gewichte auf die Größe des Glühens zu bringen, müssen Sie wahrscheinlich das EdgeShapeFunctionund verwenden VertexShapeFunction. Ich habe eine Werbetafel-Textur eines Linseneffekts mit Alpha erstellt und dieses Bild für die Scheitelpunkte verwendet:

Ich habe auch den Kantenglüheffekt verwendet, den Sie in der Frage erwähnt haben, die die Linien stapelt. Kanten mit mehr Gewicht sollten mehr leuchten, und Scheitelpunkte mit mehr Gewicht haben eine größere Streulicht:

SeedRandom[123];
G = SpatialGraphDistribution[100, 0.20];
g = RandomGraph[G];
glowtexture = Import["lensbb.png"];
edgeWeights = RandomReal[1, EdgeCount[g]];
vertexWeights = RandomReal[1, VertexCount[g]];

edgeShapeFunc = 
  With[{weight = AnnotationValue[{g, #2}, EdgeWeight]}, 
    Table[{RGBColor[0.7, 1.0, 0.9], Opacity[1/k^1.3], 
      Thickness[.001 k*weight], CapForm["Round"], Line[#1]}, {k, 20}]] &;

vertexShapeFunc = 
  With[{weight = AnnotationValue[{g, #2}, VertexWeight]}, 
    Inset[glowtexture, #1, Center, weight*0.3]] &;

g = Graph[g, EdgeWeight -> edgeWeights, VertexWeight -> vertexWeights,
   VertexShapeFunction -> vertexShapeFunc, Background -> Black, 
  EdgeShapeFunction -> edgeShapeFunc, PlotRangePadding -> .1]

Anstatt den obigen Trick zum Stapeln von Linien / Deckkraft zu verwenden, um die leuchtenden Kanten zu erzeugen, können Sie stattdessen auch strukturierte Polygone verwenden. Dies ist schneller, aber ein Nachteil ist, wenn die Kanten zu dick werden, die Kappen sichtbar und hässlich sind:

g = Graph[UndirectedEdge @@@ {{1, 2}, {2, 3}, {3, 1}}];
edgeWeights = {1, 2, 3}/6.;
vertexWeights = {1, 2, 3}/6.;

glowtexture = Import["lensbb.png"];
edgegradimg = LinearGradientImage[{Transparent,Cyan,Transparent}, {64,64}];

edgeShapeFunc = 
  Module[{weight = AnnotationValue[{g, #2}, EdgeWeight], s = 1/10., 
     vec = #1[[2]] - #1[[1]], perp},
    perp = Cross[vec];
    {Texture[edgegradimg], 
     Polygon[{
         #1[[1]]-perp*weight*s, 
         #1[[1]]+perp*weight*s,
         #1[[2]]+perp*weight*s,
         #1[[2]]-perp*weight*s
     }, VertexTextureCoordinates -> {{0,0},{1,0},{1,1},{0,1}}]
    }] &;

vertexShapeFunc = 
  With[{weight = AnnotationValue[{g, #2}, VertexWeight]}, 
    Inset[glowtexture, #1, Center, weight*3]] &;

g = Graph[g, EdgeWeight -> edgeWeights, VertexWeight -> vertexWeights,
   VertexShapeFunction -> vertexShapeFunc, Background -> Black, 
  EdgeShapeFunction -> edgeShapeFunc, PlotRangePadding -> .5]

8 C.E. Aug 19 2020 at 14:37

DistanceTransform gibt uns eine Entfernungskarte des Typs, den wir zum Leuchten benötigen.

Zuerst definieren wir die Lichtquelle:

bg = ConstantImage[White, 200];
line = HighlightImage[
  bg, {
   Black,
   Thick,
   Line[{{50, 100}, {150, 100}}]
   }]

Als nächstes berechnen wir die Entfernungstransformation. Wir skalieren es so, dass 1 im resultierenden Bild der Diagonale des Bildes entspricht.

glow = ColorNegate@Image[Divide[
     ImageData@DistanceTransform[line],
     200 Sqrt[2]
     ]^0.2]

Die Zahl 0,2 steuert, wie schnell das Leuchten nachlässt.

Als nächstes können wir dem Glühen eine Farbe zuweisen:

glow ConstantImage[Red, 200]

Und wir können sogar Farbfunktionen anwenden:

ImageApply[List @@ ColorData["AvocadoColors", #] &, glow]

Das Erstellen einer schönen Farbfunktion ist der Schlüssel, um ein schönes Leuchten wie in Ihrem Beispiel zu erzeugen.

Das Erstellen eines leuchtenden Diagramms ist mit dieser Technik recht einfach. Jede Kante ist eine Linie und jeder Scheitelpunkt ist ein Punkt oder eine Scheibe. Am Ende können wir sie zu einem Bild zusammenfügen.

Ich überlasse es dem Leser, eine robuste Funktion dafür zu erstellen. Ich werde nur ein kleines Beispiel machen.

Wir werden das Pappus-Diagramm für das Beispiel verwenden:

embedding = First@GraphData["PappusGraph", "Embeddings"];
coords = List @@@ GraphData["PappusGraph", "Edges"] /. Thread[
    Range[Length[embedding]] -> embedding
    ];
Graphics[{
  Point[embedding],
  Line[coords]
  }]

Um es auf ein Bild anstatt in eine Grafik zu zeichnen, müssen die Koordinaten neu skaliert werden:

toImageCoordinates[{x_, y_}] := {
  Rescale[x, {-1, 1}, {0, 200}],
  Rescale[y, {-1, 1}, {0, 200}]
  }

primitives = Join[
   Point@*toImageCoordinates /@ embedding,
   Line@*toImageCoordinates /@ coords
   ];

Diese Funktion zeichnet jedes Grundelement mit einem Glühen:

draw[primitive_, size_, glow_] := Module[{bg, img},
  bg = ConstantImage[White, 200];
  img = HighlightImage[bg, {
     Black,
     PointSize[Large],
     Thick,
     primitive
     }];
  ColorNegate@Image[Divide[
      ImageData@DistanceTransform[img],
      size Sqrt[2]
      ]^glow]
  ]

draw[First@primitives, 200, 0.2]

Nun ist geplant, diese Funktion auf alle Grundelemente abzubilden.

images = draw[#, 200, 0.2] & /@ primitives;
ImageAdd @@ images // ImageAdjust

Daraus ist ersichtlich, dass Kanten und Punkte unterschiedlich stark leuchten können. Aus Zeitgründen werde ich die Funktion, die all dies zusammenfasst, nicht zu einer "leuchtenden Grafik" -Funktion machen, aber ich lasse dies hier als möglichen Ansatz zur Lösung dieses Problems.