Thema: Graphzeichnen

Einzelnen Beitrag anzeigen

Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#5

AW: Graphzeichnen

  Alt 5. Jun 2013, 23:43
Ich habe sowas auch mal in C# programmiert, das geht eigentlich ohne großen Aufwand straightforward.

In den Kanten hatte ich eine "Step" Methode, die die angrenzenden Knoten versetzt hat (.Delta) je nach "Federkraft":
Code:
// Federkraft wirkt auf die Knoten
internal void Step(float factor, float length)
{
   var dx = TargetNode.Position.X - SourceNode.Position.X;
   var dy = TargetNode.Position.Y - SourceNode.Position.Y;
   Vector distance = new Vector(dx, dy);
   double force = (distance.Length - length * Cost) * factor;
   distance.Normalize();

   SourceNode.Delta += distance * force;
   TargetNode.Delta -= distance * force;
}
In den Knoten dann das quadratische Abstoßungsgesetz:
Code:
// Abstoßung der Knoten nach dem Coulomb-Gesetz
internal void Step(Node other, float factor)
{
   double dx = other.Position.X - Position.X;
   double dy = other.Position.Y - Position.Y;

   if (dx == 0 && dy == 0) // Wenn 2 Knoten exakt die gleiche Position haben kommt es sonst zu einem Fehler
      dx = 1;

   Vector distance = new Vector(dx, dy);
   double force = factor / distance.LengthSquared; // F = k * r^-2
   distance.Normalize();

   Delta -= distance * force;
   other.Delta += distance * force;
}
Und wenn du bis hierher gleesen hast: Jetzt kommt der interessante Teil. Nach meine damaligen Einschätzung hat sich der Graph nämlich noch sehr oft "verhakt" und ist quasi in lokalen Energieminima stecken geblieben. Knoten die eigentlich nach außen gelegt werden konnten, kamen nicht über die Barriere hinaus, die von dern anderen Knoten gebildet wurde. Deshalb habe ich eine angepasste Version entwickelt, die zuerst die Knotenabstoßungen übermächtig macht und den Graphen demzufolge komplett "aufbläht". Die Knoten können sich dann passend auseinanderfriemeln. Die Kräfte lassen dann mit einer Parabelfunktion stark nach und werden schließlich gleich den eingestellten Kräften. Schaut dann so aus:

Code:
public void AutoLayout(float NodeForcefactor, float EdgeForgeFactor, float EdgeRelaxedLength)
{
   const int loops = 800; // Layout-Iterationen
   const int nf_elev = 300; // Erhöhungsfaktor für die Knotenkräfte am Anfang

   for (int i = 0; i < loops; i++)
   {
      foreach (var edge in Edges)
      {
         edge.Step(EdgeForgeFactor, EdgeRelaxedLength); // Federkräfte auf die Knoten wirken lassen
      }
                // Die Knoten abstoßen lassen
      for (int a = 0; a < Nodes.Count(); a++)
      {
         for (int b = a + 1; b < Nodes.Count(); b++)
         {
            float x = 1 - (float)i / loops;
            Nodes[a].Step(Nodes[b], NodeForcefactor * (1 + x * x * nf_elev));
         }
      }
      foreach (var node in Nodes)
      {
         node.Layout(); // Anhand der Vektorsumme der Kräfte jeden Knoten passend verschieben.
      }
   }
}
Vielleicht kannst du damit ja etwas anfangen
Die Ergebnisse des Auto-Layouts kannst du im Anhang betrachten. Du kannst ein paar Sachen im Kontextmenü machen wenn du auf Knoten/Kanten klickst. Mit Bearbeiten kannst du rechts auch die Kosten verändern, falls du nicht alle Kanten gleich lang haben möchtest....
Angehängte Dateien
Dateityp: zip graph_demo.zip (18,5 KB, 22x aufgerufen)

Geändert von jfheins ( 5. Jun 2013 um 23:48 Uhr)
  Mit Zitat antworten Zitat