กราฟถ่วงน้ำหนักเรืองแสง (เครือข่าย): จุดยอดและขอบ

Aug 19 2020

ฉันกำลังพยายามหาวิธี (เรียบง่ายและประสิทธิภาพ / ความเร็วที่ดีที่สุดสำหรับกราฟขนาดใหญ่) เพื่อทำสิ่งต่อไปนี้:

  • จุดยอดกราฟจัดแต่งทรงผมตามเอฟเฟกต์เรืองแสงและความเข้มขึ้นอยู่กับ VertexWeight

  • จัดแต่งทรงขอบกราฟตามเอฟเฟกต์เรืองแสงและความเข้มขึ้นอยู่กับ EdgeWeight

  • DirectedEdgeสไตล์เอฟเฟกต์เรืองแสงก็เป็นที่ต้องการเช่นกัน (ในขณะที่สิ่งที่เรียบง่ายสามารถเริ่มต้นได้ที่UndirectedEdge)

ตัวอย่างเช่นสิ่งนี้:

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

ฉันกำลังมองหาภาพที่คล้ายกับภาพด้านล่างนี้ยกเว้นว่าขอบจะต้องเรืองแสงด้วย:

ปัญหาที่ฉันประสบ

1. ใช้งานง่ายของการเรืองแสงที่น่าทึ่ง

ฉันเคยเห็นเอฟเฟกต์เรืองแสงหลายแบบ (รวมถึงสิ่งนี้เกี่ยวกับจุดเรืองแสง) แต่ไม่ใช่ผู้เชี่ยวชาญเกี่ยวกับแนวคิดด้านภาพเทียบกับประสิทธิภาพที่ดีที่สุด น่าแปลกที่ฉันไม่ได้เห็นเส้นเรืองแสงรอบ ๆ มากนัก ฉันจะเริ่มต้นด้วยสิ่งนี้อย่างไร้เดียงสา แต่นั่นอาจจะดีขึ้นด้วยสายตาและประสิทธิภาพที่ชาญฉลาด:

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. ส่งน้ำหนักให้เรืองแสง

ในขณะที่ฉันรับรู้VertexShapeFunctionและEdgeShapeFunctionฉันไม่ค่อยแน่ใจว่าจะส่งน้ำหนักให้พวกเขาอย่างเหมาะสมที่สุดได้อย่างไร ... และหากคุณสมบัติเหล่านี้เป็นแนวทางที่ถูกต้อง

เรืองแสงในฟังก์ชั่นในตัว

ฉันสังเกตเห็นว่าฟังก์ชั่นเหล่านี้ทำให้เกิดแสง:

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

และตามที่สังเกตเห็นโดย @EC ในคำตอบของเขาด้านล่างเช่น

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

ขอบคุณความช่วยเหลือของคุณจะได้รับการชื่นชมอย่างมาก!

คำตอบ

14 flinty Aug 19 2020 at 22:41

คุณสามารถรับเอฟเฟกต์การเรืองแสงโดยรวมได้โดยImageAddสำเนาเบลอของมาสก์รูปภาพ เป็นที่ยอมรับว่ามันค่อนข้างธรรมดา แต่เอฟเฟกต์นั้นน่าสนใจ ฉันเลือกที่จะสร้างเครือข่าย 'สมอง' โดยใช้AnatomyDataและNearestNeighbourGraphทำให้ดูเหมือนเป็นการตลาด AI ที่เกินจริง:

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

เพื่อให้ได้น้ำหนักที่จะส่งผลกระทบต่อขนาดของเรืองแสงคุณอาจจะต้องใช้และEdgeShapeFunction VertexShapeFunctionฉันสร้างพื้นผิวป้ายโฆษณาของเอฟเฟกต์เลนส์ด้วยอัลฟาและฉันใช้ภาพนี้สำหรับจุดยอด:

ฉันยังใช้เอฟเฟกต์การเรืองแสงที่ขอบที่คุณพูดถึงในคำถามที่ซ้อนเส้น ขอบที่มีน้ำหนักมากกว่าควรมีการเรืองแสงมากขึ้นและจุดยอดที่มีน้ำหนักมากกว่าจะมีแสงแฟลร์ขนาดใหญ่:

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]

แทนที่จะใช้เคล็ดลับการซ้อนเส้น / ความทึบด้านบนเพื่อสร้างขอบเรืองแสงคุณสามารถใช้รูปหลายเหลี่ยมที่มีพื้นผิวแทนได้ เร็วกว่า แต่ข้อเสียคือเมื่อขอบหนาเกินไปทำให้มองเห็นตัวพิมพ์ใหญ่และน่าเกลียด:

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 ให้แผนที่ระยะทางของประเภทที่เราต้องการสำหรับการเรืองแสง

ก่อนอื่นเรากำหนดแหล่งกำเนิดแสง:

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

ต่อไปเราจะคำนวณการแปลงระยะทาง เราปรับขนาดให้ 1 ในภาพที่ได้นั้นตรงกับเส้นทแยงมุมของภาพ

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

หมายเลข 0.2 ควบคุมความเร็วของการเรืองแสง

ต่อไปเราสามารถใช้สีกับเรืองแสงได้:

glow ConstantImage[Red, 200]

และเรายังสามารถใช้ฟังก์ชันสี:

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

การสร้างฟังก์ชันสีที่สวยงามจะเป็นกุญแจสำคัญในการสร้างแสงที่สวยงามเหมือนในตัวอย่างของคุณ

การสร้างกราฟเรืองแสงค่อนข้างตรงไปตรงมาโดยใช้เทคนิคนี้ ขอบทุกเส้นเป็นเส้นและจุดยอดทุกจุดคือจุดหรือดิสก์ ในที่สุดเราก็สามารถรวมเป็นภาพเดียวได้

ฉันจะปล่อยให้ผู้อ่านสร้างฟังก์ชันที่มีประสิทธิภาพสำหรับสิ่งนี้ ฉันจะทำเป็นตัวอย่างเล็ก ๆ

เราจะใช้กราฟ Pappus เป็นตัวอย่าง:

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

การวาดลงบนรูปภาพแทนที่จะเป็นในกราฟิกจำเป็นต้องมีการปรับขนาดพิกัด:

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

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

ฟังก์ชั่นนี้จะวาดแบบดั้งเดิมด้วยการเรืองแสง:

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]

ตอนนี้แผนคือการแมปฟังก์ชันนี้กับสิ่งดั้งเดิมทั้งหมด

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

จากสิ่งนี้เห็นได้ชัดว่าขอบและจุดต่างๆอาจมีปริมาณแสงที่แตกต่างกัน เนื่องจากข้อ จำกัด ด้านเวลาฉันจะไม่ทำให้ฟังก์ชันที่รวมทั้งหมดนี้เข้าด้วยกันเป็นฟังก์ชัน "กราฟเรืองแสง" แต่ฉันปล่อยให้สิ่งนี้เป็นแนวทางที่เป็นไปได้ในการแก้ปัญหานี้