グラフの視覚化。 リブ結合方法

構造が見えるように、グラフをグラフ形式で表示すると便利な場合があります。 これが役立ついくつかの例があります:いくつかのプログラムのソースコードのクラスとパッケージの階層の視覚化、ソーシャルグラフ(同じTwitterまたはFacebook)または引用グラフ(出版物が誰を指すか)の視覚化など。 しかし、ここに問題があります:グラフ内のエッジの数はしばしば非常に大きいため、描画されたグラフを解析することは単に不可能です。 この写真を見てください:







これは、一部のソフトウェアシステムの依存関係グラフです。 これは、パケット化のツリー(灰色のボール-パッケージ、白いボール-クラス)であり、いくつかのクラスが他のクラスに依存するエッジが重ねられています。 方向矢印を描画しないために、エッジはグラデーションラインの形で描画されます。緑はエッジの始まりで、赤はエッジの終わりです。 ご覧のとおり、グラフは視覚的に過負荷になっているため、プログラムのアーキテクチャをトレースできません。

この問題を解決する方法の猫の説明の下で。





メソッドの説明



この方法のアイデアは非常にシンプルです-ケーブルネットワークと同じ原理を使用してみませんか? 私たちの多くは、配線が多すぎるときにどの混乱が始まるかを知っています。 この混乱を防ぐために、ワイヤは束ねられています。

このアイデアをグラフに適用し、アルゴリズムを実装する方法は?



例を使用して1つのエッジを描画する例を考えてみましょう。 頂点P 0から頂点P 4にエッジを描く必要があります。







これらのピーク間のパスを見つけます。







ここで、点P 0 、P 1 、P 2 、P 3 、P 4によって形成される多角形を通る曲線を描きます。







実際に、それを行う方法は? この方法の作成者は、区分的に定義された3次Bスプラインが視覚化の目的の曲線として最適であることを分析しました。 このようなBスプラインを見つける問題は古典的であり、他の人によって長い間解決されてきました。 つまり、3次Bスプラインは、エッジでの1次および2次導関数のマッチング条件が満たされている2次元空間内の3次曲線の単なるコレクションであると言えます。 誰かがこのメソッドを実装したい場合、ホイールを再発明しないでください-最後に、C#でエッジをレンダリングするための作業コードを投稿しました。

rib骨の連結度を制御するために、0から1の値を取るパラメーターβが導入されています(0-rib骨は束に接続されておらず、独立した直線です、1-rib骨は可能な限り互いに接続されています)。 数学的には、次のようになります。









S(t)はスプラインポイント、S '(t)は画面上にエッジとして描画される結果の曲線です。

さて、ここにすべてのこの数学の結果の結果があります:







ここで、β= 0.85。 これで、プログラムのアーキテクチャがはるかに明確になりました。 エッジの別個のバンドルが表示され、パッケージ間の依存関係が表示されます。

同じグラフ、アーキテクチャの視覚化の放射状方法のみが使用されます(方法を適用しない場合、β= 0と同等です):







メソッドの使用(β= 0.85):







ご覧のとおり、エッジをバインドする方法は、ツリーを描画するさまざまな方法と組み合わせることができます。これは大きな利点です。



他の例



引用文献を数えます。 緑の円は、前年(2007、2006など)の出版物を引用した2008年の出版物です。 2008年の出版物からのリンクのみが記載されています。 メソッドを適用しない場合、次のようになります。





メソッドの使用(β= 0.99):







備考



1.これを回避するには:







アルファチャネルを使用できます。







2. LCA頂点を制御ポリゴン(最も一般的な祖先、接続された頂点間の最も一般的な祖先)から削除すると、グラフの外観が非常に良くなります。 違いを見てみましょう:







2番目の場合、LCA頂点は頂点セットから除外され、グラフはより美しく見えます。



C#実装



以下は、拡張クラスSystem.Drawing.Graphicsの実装です。これは、頂点のセットを介して3次Bスプラインを描画できます。



using System;

using System.Text;

using System.Drawing;



static class GraphicsExtension

{

private static void DrawCubicCurve( this Graphics graphics, Pen pen, float beta, float step, PointF start, PointF end, float a3, float a2, float a1, float a0, float b3, float b2, float b1, float b0)

{

float xPrev, yPrev;

float xNext, yNext;

bool stop = false ;



xPrev = beta * a0 + (1 - beta) * start.X;

yPrev = beta * b0 + (1 - beta) * start.Y;



for ( float t = step; ; t += step)

{

if (stop)

break ;



if (t >= 1)

{

stop = true ;

t = 1;

}



xNext = beta * (a3 * t * t * t + a2 * t * t + a1 * t + a0) + (1 - beta) * (start.X + (end.X - start.X) * t);

yNext = beta * (b3 * t * t * t + b2 * t * t + b1 * t + b0) + (1 - beta) * (start.Y + (end.Y - start.Y) * t);



graphics.DrawLine(pen, xPrev, yPrev, xNext, yNext);



xPrev = xNext;

yPrev = yNext;

}

}



/// <summary>

/// Draws a B-spline curve through a specified array of Point structures.

/// </summary>

/// <param name="pen">Pen for line drawing.</param>

/// <param name="points">Array of control points that define the spline.</param>

/// <param name="beta">Bundling strength, 0 <= beta <= 1.</param>

/// <param name="step">Step of drawing curve, defines the quality of drawing, 0 < step <= 1</param>

internal static void DrawBSpline( this Graphics graphics, Pen pen, PointF[] points, float beta, float step)

{

if (points == null )

throw new ArgumentNullException( "The point array must not be null." );



if (beta < 0 || beta > 1)

throw new ArgumentException( "The bundling strength must be >= 0 and <= 1." );



if (step <= 0 || step > 1)

throw new ArgumentException( "The step must be > 0 and <= 1." );



if (points.Length <= 1)

return ;



if (points.Length == 2)

{

graphics.DrawLine(pen, points[0], points[1]);

return ;

}



float a3, a2, a1, a0, b3, b2, b1, b0;

float deltaX = (points[points.Length - 1].X - points[0].X) / (points.Length - 1);

float deltaY = (points[points.Length - 1].Y - points[0].Y) / (points.Length - 1);

PointF start, end;



{

a0 = points[0].X;

b0 = points[0].Y;



a1 = points[1].X - points[0].X;

b1 = points[1].Y - points[0].Y;



a2 = 0;

b2 = 0;



a3 = (points[0].X - 2 * points[1].X + points[2].X) / 6;

b3 = (points[0].Y - 2 * points[1].Y + points[2].Y) / 6;



start = points[0];

end = new PointF

(

points[0].X + deltaX,

points[0].Y + deltaY

);



graphics.DrawCubicCurve(pen, beta, step, start, end, a3, a2, a1, a0, b3, b2, b1, b0);

}



for ( int i = 1; i < points.Length - 2; i++)

{

a0 = (points[i - 1].X + 4 * points[i].X + points[i + 1].X) / 6;

b0 = (points[i - 1].Y + 4 * points[i].Y + points[i + 1].Y) / 6;



a1 = (points[i + 1].X - points[i - 1].X) / 2;

b1 = (points[i + 1].Y - points[i - 1].Y) / 2;



a2 = (points[i - 1].X - 2 * points[i].X + points[i + 1].X) / 2;

b2 = (points[i - 1].Y - 2 * points[i].Y + points[i + 1].Y) / 2;



a3 = (-points[i - 1].X + 3 * points[i].X - 3 * points[i + 1].X + points[i + 2].X) / 6;

b3 = (-points[i - 1].Y + 3 * points[i].Y - 3 * points[i + 1].Y + points[i + 2].Y) / 6;



start = new PointF

(

points[0].X + deltaX * i,

points[0].Y + deltaY * i

);



end = new PointF

(

points[0].X + deltaX * (i + 1),

points[0].Y + deltaY * (i + 1)

);



graphics.DrawCubicCurve(pen, beta, step, start, end, a3, a2, a1, a0, b3, b2, b1, b0);

}



{

a0 = points[points.Length - 1].X;

b0 = points[points.Length - 1].Y;



a1 = points[points.Length - 2].X - points[points.Length - 1].X;

b1 = points[points.Length - 2].Y - points[points.Length - 1].Y;



a2 = 0;

b2 = 0;



a3 = (points[points.Length - 1].X - 2 * points[points.Length - 2].X + points[points.Length - 3].X) / 6;

b3 = (points[points.Length - 1].Y - 2 * points[points.Length - 2].Y + points[points.Length - 3].Y) / 6;



start = points[points.Length - 1];



end = new PointF

(

points[0].X + deltaX * (points.Length - 2),

points[0].Y + deltaY * (points.Length - 2)

);



graphics.DrawCubicCurve(pen, beta, step, start, end, a3, a2, a1, a0, b3, b2, b1, b0);

}

}

}



* This source code was highlighted with Source Code Highlighter .








文学



説明されているエッジとほとんどすべての写真をバインドする方法は、記事「Hierarchical Edge Bundles:

階層データの隣接関係の可視化。」 ダニー・ホルテン投稿



All Articles