Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Layout/StandardTreeLayout.cs @ 18242

Last change on this file since 18242 was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

File size: 14.4 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Drawing;
5using Netron.Diagramming.Core.Analysis;
6namespace Netron.Diagramming.Core {
7  /// <summary>
8  /// <para>TreeLayout that computes a tidy layout of a node-link tree
9  ///  diagram. This algorithm lays out a rooted tree such that each
10  ///  depth level of the tree is on a shared line. The orientation of the
11  ///  tree can be set such that the tree goes left-to-right (default),
12  ///  right-to-left, top-to-bottom, or bottom-to-top.</para>
13  /// 
14  ///  <para>The algorithm used is that of Christoph Buchheim, Michael Jünger,
15  ///  and Sebastian Leipert from their research paper
16  ///  <a href="http://citeseer.ist.psu.edu/buchheim02improving.html">
17  ///  Improving Walker's Algorithm to Run in Linear Time</a>, Graph Drawing 2002.
18  ///  This algorithm corrects performance issues in Walker's algorithm, which
19  ///  generalizes Reingold and Tilford's method for tidy drawings of trees to
20  ///  support trees with an arbitrary number of children at any given node.</para>
21  /// </summary>
22  class StandardTreeLayout : TreeLayoutBase {
23
24
25
26    #region Fields
27    BackgroundWorker worker;
28    private static TreeOrientation mOrientation = TreeOrientation.TopBottom;  // the orientation of the tree
29    private static float mBreadth = 50F;   // the spacing between sibling nodes
30    private static float mSubTreeSpacing = 50F;  // the spacing between subtrees
31    private static float mDepth = 50F;  // the spacing between depth levels
32    private float m_offset = 50;  // pixel offset for root node position
33    private PointF m_anchor;
34    private PointF m_tmpa;
35    private float[] m_depths = new float[10];
36    private int mMaxDepth = 0;
37    private Dictionary<string, Params> Pars;
38    private float m_ax, m_ay; // for holding anchor co-ordinates
39    #endregion
40
41    #region Properties
42    /// <summary>
43    /// Gets or sets the orientation of the tree.
44    /// </summary>
45    /// <value>The orientation.</value>
46    public static TreeOrientation Orientation {
47      get {
48        return mOrientation;
49      }
50      set {
51        mOrientation = value;
52      }
53    }
54    /// <summary>
55    /// Gets or sets the depth spacing.
56    /// </summary>
57    /// <value>The depth spacing.</value>
58    public static float DepthSpacing {
59      get { return mDepth; }
60      set { mDepth = value; }
61    }
62
63    /// <summary>
64    /// Gets or sets the breadth spacing.
65    /// </summary>
66    /// <value>The breadth spacing.</value>
67    public static float BreadthSpacing {
68      get { return mBreadth; }
69      set { mBreadth = value; }
70    }
71    /// <summary>
72    /// Gets or sets the spacing between subtrees.
73    /// </summary>
74    /// <value>The sub tree spacing.</value>
75    public static float SubTreeSpacing {
76      get { return mSubTreeSpacing; }
77      set { mSubTreeSpacing = value; }
78
79    }
80
81    public float RootOffset {
82      get { return m_offset; }
83      set { m_offset = value; }
84    }
85
86    #endregion
87
88    #region Constructor
89    public StandardTreeLayout(IController controller)
90      : base("Standard TreeLayout", controller) {
91
92      Orientation = TreeOrientation.LeftRight;
93
94    }
95    #endregion
96
97    #region Methods
98
99    public override void Run() {
100      Run(2000);
101    }
102    public override void Run(int time) {
103      if (worker != null && worker.IsBusy)
104        worker.CancelAsync();
105      worker = new BackgroundWorker();
106      worker.WorkerSupportsCancellation = true;
107      worker.DoWork += new DoWorkEventHandler(worker_DoWork);
108      worker.RunWorkerAsync(time);
109    }
110
111    public override void Stop() {
112      if (worker != null && worker.IsBusy)
113        worker.CancelAsync();
114    }
115
116    private void worker_DoWork(object sender, DoWorkEventArgs e) {
117      this.Controller.View.Suspend();
118      Init();
119      Layout();
120      this.Controller.View.Resume();
121    }
122    /// <summary>
123    /// Runs a single step.
124    /// </summary>
125    private void RunStep() {
126
127    }
128    private bool Init() {
129
130      this.Graph = this.Model as IGraph;
131
132      if (Graph == null)
133        throw new InconsistencyException("The model has not been set and the Graph property is hence 'null'");
134
135      m_depths = new float[10];
136      this.LayoutRoot = this.Controller.Model.LayoutRoot;//could be null if not set in the GUI
137      Graph.ClearSpanningTree();
138      Graph.MakeSpanningTree(LayoutRoot as INode);
139
140      //Trace.WriteLine((LayoutRoot as INode).ChildCount);
141
142      if (Graph.SpanningTree == null)
143        throw new InconsistencyException("The spanning tree is not set (se the root of the tree layout first)");
144
145
146
147
148
149      Pars = new Dictionary<string, Params>();
150      if (Graph.Nodes.Count == 0)
151        return false;
152      if (Graph.Edges.Count == 0) //this layout is base on embedded springs in the connections
153        return false;
154
155
156      Params par;
157
158      foreach (INode node in Graph.Nodes) {
159        par = new Params();
160        par.init(node);
161        Pars.Add(node.Uid.ToString(), par);
162      }
163      return true;
164    }
165    public PointF LayoutAnchor {
166      get {
167        //if (m_anchor != null)
168        //{
169        //    return m_anchor;
170        //}
171
172        m_tmpa = PointF.Empty;
173        if (Graph != null) {
174          Rectangle b = Bounds;
175
176          switch (mOrientation) {
177            case TreeOrientation.LeftRight:
178              m_tmpa = new PointF(m_offset, b.Height / 2F);
179              break;
180            case TreeOrientation.RightLeft:
181              m_tmpa = new PointF(b.Width - m_offset, b.Height / 2F);
182              break;
183            case TreeOrientation.TopBottom:
184              m_tmpa = new PointF(b.Width / 2F, m_offset);
185              break;
186            case TreeOrientation.BottomTop:
187              m_tmpa = new PointF(b.Width / 2F, b.Height - m_offset);
188              break;
189            case TreeOrientation.Center:
190              m_tmpa = new PointF(b.Width / 2F, b.Height / 2F);
191              break;
192            default:
193              break;
194          }
195          //TODO: set the center of the layout here on the view
196          //d.getInverseTransform().transform(m_tmpa, m_tmpa);
197        }
198        return m_tmpa;
199      }
200      set {
201        m_anchor = value;
202      }
203    }
204
205    /// <summary>
206    /// Returns the location of halfway the two given nodes
207    /// </summary>
208    /// <param name="l"></param>
209    /// <param name="r"></param>
210    /// <param name="siblings"></param>
211    /// <returns></returns>
212    private double spacing(INode l, INode r, bool siblings) {
213      bool w = (mOrientation == TreeOrientation.TopBottom || mOrientation == TreeOrientation.BottomTop);
214      return (siblings ? mBreadth : mSubTreeSpacing) + 0.5 * (w ? l.Rectangle.Width + r.Rectangle.Width : l.Rectangle.Height + r.Rectangle.Height);
215    }
216    private void updateDepths(int depth, INode item) {
217      bool v = (mOrientation == TreeOrientation.TopBottom || mOrientation == TreeOrientation.BottomTop);
218      double d = (v ? item.Rectangle.Height : item.Rectangle.Width);
219      if (m_depths.Length <= depth)
220        Array.Resize<float>(ref m_depths, (int)(3 * depth / 2));
221      m_depths[depth] = (float)Math.Max(m_depths[depth], d);
222      mMaxDepth = (int)Math.Max(mMaxDepth, depth);
223    }
224
225    private void determineDepths() {
226      for (int i = 1; i < mMaxDepth; ++i)
227        m_depths[i] += m_depths[i - 1] + mDepth;
228    }
229
230    public void Layout() {
231      m_depths.Initialize();
232
233      mMaxDepth = 0;
234
235      PointF a = LayoutAnchor;
236      m_ax = a.X;
237      m_ay = a.Y;
238
239      INode root = LayoutRoot as INode;
240      Params rp = Pars[root.Uid.ToString()];
241
242      // do first pass - compute breadth information, collect depth info
243      firstWalk(root, 0, 1);
244
245      // sum up the depth info
246      determineDepths();
247
248      // do second pass - assign layout positions
249      secondWalk(root, null, -rp.prelim, 0);
250    }
251
252    private void firstWalk(INode n, int num, int depth) {
253      //Trace.WriteLine("depthj: " + depth);
254      Params np = Pars[n.Uid.ToString()];
255
256      np.number = num;
257      updateDepths(depth, n);
258
259      bool expanded = n.IsExpanded;
260      if (n.ChildCount == 0 || !expanded) // is leaf
261            {
262        INode l = (INode)n.PreviousSibling;
263        if (l == null) {
264          np.prelim = 0;
265        } else {
266          np.prelim = Pars[l.Uid.ToString()].prelim + spacing(l, n, true);
267        }
268      } else if (expanded) {
269        INode leftMost = n.FirstChild;
270        INode rightMost = n.LastChild;
271        INode defaultAncestor = leftMost;
272        INode c = leftMost;
273        for (int i = 0; c != null; ++i, c = c.NextSibling) {
274          firstWalk(c, i, depth + 1);
275          defaultAncestor = apportion(c, defaultAncestor);
276        }
277
278        executeShifts(n);
279
280        double midpoint = 0.5 *
281            (Pars[leftMost.Uid.ToString()].prelim + Pars[rightMost.Uid.ToString()].prelim);
282
283        INode left = (INode)n.PreviousSibling;
284        if (left != null) {
285          np.prelim = Pars[left.Uid.ToString()].prelim + spacing(left, n, true);
286          np.mod = np.prelim - midpoint;
287        } else {
288          np.prelim = midpoint;
289        }
290      }
291    }
292    private INode apportion(INode v, INode a) {
293      INode w = (INode)v.PreviousSibling;
294      if (w != null) {
295        INode vip, vim, vop, vom;
296        double sip, sim, sop, som;
297
298        vip = vop = v;
299        vim = w;
300        vom = (INode)vip.ParentNode.FirstChild;
301
302        sip = Pars[vip.Uid.ToString()].mod;
303        sop = Pars[vop.Uid.ToString()].mod;
304        sim = Pars[vim.Uid.ToString()].mod;
305        som = Pars[vom.Uid.ToString()].mod;
306        Params parms;
307        INode nr = nextRight(vim);
308        INode nl = nextLeft(vip);
309        while (nr != null && nl != null) {
310          vim = nr;
311          vip = nl;
312          vom = nextLeft(vom);
313          vop = nextRight(vop);
314          parms = Pars[vop.Uid.ToString()];
315          parms.ancestor = v;
316          double shift = (Pars[vim.Uid.ToString()].prelim + sim) -
317              (Pars[vip.Uid.ToString()].prelim + sip) + spacing(vim, vip, false);
318          if (shift > 0) {
319            moveSubtree(ancestor(vim, v, a), v, shift);
320            sip += shift;
321            sop += shift;
322          }
323          sim += Pars[vim.Uid.ToString()].mod;
324          sip += Pars[vip.Uid.ToString()].mod;
325          som += Pars[vom.Uid.ToString()].mod;
326          sop += Pars[vop.Uid.ToString()].mod;
327
328          nr = nextRight(vim);
329          nl = nextLeft(vip);
330        }
331        if (nr != null && nextRight(vop) == null) {
332          Params vopp = Pars[vop.Uid.ToString()];
333          vopp.thread = nr;
334          vopp.mod += sim - sop;
335        }
336        if (nl != null && nextLeft(vom) == null) {
337          Params vomp = Pars[vom.Uid.ToString()];
338          vomp.thread = nl;
339          vomp.mod += sip - som;
340          a = v;
341        }
342      }
343      return a;
344    }
345    private INode nextLeft(INode n) {
346      INode c = null;
347      if (n.IsExpanded) c = n.FirstChild;
348      return (c != null ? c : Pars[n.Uid.ToString()].thread);
349    }
350
351    private INode nextRight(INode n) {
352      INode c = null;
353      if (n.IsExpanded) c = n.LastChild;
354      return (c != null ? c : Pars[n.Uid.ToString()].thread);
355    }
356
357    private void moveSubtree(INode wm, INode wp, double shift) {
358      Params wmp = Pars[wm.Uid.ToString()];
359      Params wpp = Pars[wp.Uid.ToString()];
360      double subtrees = wpp.number - wmp.number;
361      wpp.change -= shift / subtrees;
362      wpp.shift += shift;
363      wmp.change += shift / subtrees;
364      wpp.prelim += shift;
365      wpp.mod += shift;
366    }
367
368    private void executeShifts(INode n) {
369      double shift = 0, change = 0;
370      for (INode c = n.LastChild; c != null; c = c.PreviousSibling) {
371        Params cp = Pars[c.Uid.ToString()];
372        cp.prelim += shift;
373        cp.mod += shift;
374        change += cp.change;
375        shift += cp.shift + change;
376      }
377    }
378
379    private INode ancestor(INode vim, INode v, INode a) {
380      INode p = (INode)v.ParentNode;
381      Params vimp = Pars[vim.Uid.ToString()];
382      if (vimp.ancestor != null && vimp.ancestor.ParentNode == p) {
383        return vimp.ancestor;
384      } else {
385        return a;
386      }
387    }
388
389    private void secondWalk(INode n, INode p, double m, int depth) {
390      Params np = Pars[n.Uid.ToString()];
391      setBreadth(n, p, np.prelim + m);
392      setDepth(n, p, m_depths[depth]);
393
394      if (n.IsExpanded) {
395        depth += 1;
396        for (INode c = n.FirstChild; c != null; c = c.NextSibling) {
397          if (worker.CancellationPending)
398            break;
399          secondWalk(c, n, m + np.mod, depth);
400        }
401      }
402
403      np.clear();
404    }
405
406    private void setBreadth(INode n, INode p, double b) {
407      switch (mOrientation) {
408        case TreeOrientation.LeftRight:
409        case TreeOrientation.RightLeft:
410          setY(n, p, m_ay + b);
411          break;
412        case TreeOrientation.TopBottom:
413        case TreeOrientation.BottomTop:
414          setX(n, p, m_ax + b);
415          break;
416        default:
417          throw new InconsistencyException();
418      }
419    }
420
421    private void setDepth(INode n, INode p, double d) {
422      switch (mOrientation) {
423        case TreeOrientation.LeftRight:
424          setX(n, p, m_ax + d);
425          break;
426        case TreeOrientation.RightLeft:
427          setX(n, p, m_ax - d);
428          break;
429        case TreeOrientation.TopBottom:
430          setY(n, p, m_ay + d);
431          break;
432        case TreeOrientation.BottomTop:
433          setY(n, p, m_ay - d);
434          break;
435        default:
436          throw new InconsistencyException();
437      }
438    }
439
440
441    #endregion
442
443    /// <summary>
444    /// Paramter blob to temporarily keep working data of one node.
445    /// </summary>
446    class Params {
447      public double prelim;
448      public double mod;
449      public double shift;
450      public double change;
451      public int number;
452      public INode ancestor;
453      public INode thread;
454
455      public void init(INode item) {
456        ancestor = item;
457        number = -1;
458        ancestor = thread = null;
459      }
460
461      public void clear() {
462        number = -2;
463        prelim = mod = shift = change = 0;
464        ancestor = thread = null;
465      }
466    }
467  }
468
469  public enum TreeOrientation {
470    LeftRight,
471    RightLeft,
472    TopBottom,
473    BottomTop,
474    Center
475  }
476}
Note: See TracBrowser for help on using the repository browser.