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