Parlayan ağırlıklı grafik (ağ): köşeler ve kenarlar

Aug 19 2020

Aşağıdakileri yapmanın bir yolunu bulmaya çalışıyorum (tercihen basit ve daha büyük grafikler için optimize edilmiş performans / hız):

  • Grafik tepe noktalarını ışıma efekti ile şekillendirme ve VertexWeight

  • Grafik kenarlarını ışıma efekti ile şekillendirme ve buna bağlı olarak yoğunluğu EdgeWeight

  • DirectedEdgeparlama efekti şekillendirme de arzu edilir (basitlik için işler başlayabilir UndirectedEdge)

Örneğin bunun gibi bir şey için:

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

Aşağıdakine benzer bir görsel arıyorum, ancak kenarların da parlaması gerekiyor:

Yaşadığım sorunlar.

1. Çarpıcı bir ışıltının basit uygulaması

Çeşitli parlama efektleri gördüm ( parlayan noktalar hakkında BU da dahil ), ancak en iyi görsel ve performans fikirleri konusunda bir uzman değilim. Şaşırtıcı bir şekilde, etrafta parlayan çizgiler hakkında pek bir şey görmedim. Safça böyle bir şeyle başlardım, ancak bu muhtemelen görsel ve performans açısından iyileştirilebilir:

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. Ağırlıkları parlatmak

Ben farkındayım ederken VertexShapeFunctionve EdgeShapeFunctionben pek emin optimum onlara ağırlıklarını nasıl geçirileceği değilim ... ve bu özellikler doğru bir yaklaşım olup olmadığını.

Yerleşik işlevlerde parlama

Bu işlevlerin biraz parıltı ürettiğini fark ettim:

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

Ve @EC'nin aşağıdaki cevabında fark ettiği gibi

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

Teşekkürler, yardımınız çok takdir ediliyor!

Yanıtlar

14 flinty Aug 19 2020 at 22:41

ImageAddGörüntü maskesinin bulanık bir kopyası ile genel bir parlaklık efekti elde edebilirsiniz . Kuşkusuz biraz basit, ancak etki çekici. Kullanarak bir 'beyin' ağı oluşturmayı AnatomyDatave NearestNeighbourGraphonu aşırı abartılı yapay zeka pazarlama şeyine benzetmeyi seçtim :

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

Size kızdırma boyutunu etkileyecek ağırlıkları almak için muhtemelen kullanmanız gerekir EdgeShapeFunctionve VertexShapeFunction. Alfa ile bir mercek efektinin ilan tahtası dokusunu oluşturdum ve bu resmi köşeler için kullandım:

Çizgileri istifleyen soruda bahsettiğiniz kenar parlama efektini de kullandım. Daha fazla ağırlığa sahip kenarların daha fazla parlaması gerekir ve daha fazla ağırlığa sahip köşelerin daha büyük bir parlaması olur:

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]

Parlayan kenarları oluşturmak için yukarıdaki satır yığınlama / opaklık numarasını kullanmak yerine, bunun yerine dokulu çokgenler de kullanabilirsiniz. Bu daha hızlıdır, ancak bir dezavantajı, kenarlar çok kalın hale geldiğinde kapakların görünür ve çirkin olmasıdır:

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 bize ışıma için ihtiyacımız olan türde bir mesafe haritası verir.

Önce ışık kaynağını tanımlarız:

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

Sonra, mesafe dönüşümünü hesaplıyoruz. Ortaya çıkan görüntüdeki 1 görüntünün köşegenine karşılık gelecek şekilde ölçeklendiriyoruz.

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

0.2 sayısı, ışımanın ne kadar çabuk söndüğünü kontrol eder.

Ardından ışıma bir renk uygulayabiliriz:

glow ConstantImage[Red, 200]

Hatta renk fonksiyonlarını da uygulayabiliriz:

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

Güzel bir renk işlevi oluşturmak, örneğinizdeki gibi hoş bir ışıltı yaratmanın anahtarı olacaktır.

Bu tekniği kullanarak parlayan bir grafik oluşturmak oldukça basittir. Her kenar bir çizgidir ve her köşe bir nokta veya disktir. Sonunda, onları tek bir görüntüde bir araya getirebiliriz.

Bunun için sağlam bir işlev oluşturmayı okuyucuya bırakacağım. Küçük bir örnek vereceğim.

Örnek için Pappus grafiğini kullanacağız:

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

Bir grafik yerine bir görüntü üzerine çizmek, koordinatların yeniden ölçeklendirilmesini gerektirir:

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

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

Bu işlev, ışıma ile herhangi bir ilkel çizecektir:

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]

Şimdi plan, bu işlevi tüm ilkellerin haritasını çıkarmaktır.

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

Buradan, kenarların ve noktaların farklı miktarlarda parıltıya sahip olabileceği açıktır. Zaman kısıtlamaları nedeniyle, tüm bunları bir "parlayan grafik" işlevi haline getiren işlevi yapmayacağım, ancak bunu, bu sorunu çözmek için olası bir yaklaşım olarak burada bırakıyorum.