Changeset 4068 for trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Layout/StandardTreeLayout.cs
- Timestamp:
- 07/22/10 00:44:01 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Layout/StandardTreeLayout.cs
r3776 r4068 1 1 using System; 2 using System.Diagnostics; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Drawing; 3 5 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 6 namespace 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 54 261 { 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 Orientation = TreeOrientation.LeftRight; 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 m_depths = new float[10]; 155 this.LayoutRoot = this.Controller.Model.LayoutRoot;//could be null if not set in the GUI 156 Graph.ClearSpanningTree(); 157 Graph.MakeSpanningTree(LayoutRoot as INode); 158 159 //Trace.WriteLine((LayoutRoot as INode).ChildCount); 160 161 if (Graph.SpanningTree == null) 162 throw new InconsistencyException("The spanning tree is not set (se the root of the tree layout first)"); 163 164 165 166 167 168 Pars = new Dictionary<string, Params>(); 169 if (Graph.Nodes.Count == 0) 170 return false; 171 if (Graph.Edges.Count == 0) //this layout is base on embedded springs in the connections 172 return false; 173 174 175 Params par; 176 177 foreach (INode node in Graph.Nodes) 178 { 179 par = new Params(); 180 par.init(node); 181 Pars.Add(node.Uid.ToString(), par); 182 } 183 return true; 184 } 185 public PointF LayoutAnchor 186 { 187 get 188 { 189 //if (m_anchor != null) 190 //{ 191 // return m_anchor; 192 //} 193 194 m_tmpa = PointF.Empty; 195 if (Graph != null) 196 { 197 Rectangle b = Bounds; 198 199 switch (mOrientation) 200 { 201 case TreeOrientation.LeftRight: 202 m_tmpa = new PointF(m_offset, b.Height / 2F); 203 break; 204 case TreeOrientation.RightLeft: 205 m_tmpa = new PointF(b.Width - m_offset, b.Height / 2F); 206 break; 207 case TreeOrientation.TopBottom: 208 m_tmpa = new PointF(b.Width / 2F, m_offset); 209 break; 210 case TreeOrientation.BottomTop: 211 m_tmpa = new PointF(b.Width / 2F, b.Height - m_offset); 212 break; 213 case TreeOrientation.Center: 214 m_tmpa = new PointF(b.Width / 2F, b.Height / 2F); 215 break; 216 default: 217 break; 218 } 219 //TODO: set the center of the layout here on the view 220 //d.getInverseTransform().transform(m_tmpa, m_tmpa); 221 } 222 return m_tmpa; 223 } 224 set 225 { 226 m_anchor = value; 227 } 228 } 229 230 /// <summary> 231 /// Returns the location of halfway the two given nodes 232 /// </summary> 233 /// <param name="l"></param> 234 /// <param name="r"></param> 235 /// <param name="siblings"></param> 236 /// <returns></returns> 237 private double spacing(INode l, INode r, bool siblings) 238 { 239 bool w = (mOrientation == TreeOrientation.TopBottom || mOrientation == TreeOrientation.BottomTop); 240 return (siblings ? mBreadth : mSubTreeSpacing) + 0.5 * (w ? l.Rectangle.Width + r.Rectangle.Width : l.Rectangle.Height + r.Rectangle.Height); 241 } 242 private void updateDepths(int depth, INode item) 243 { 244 bool v = (mOrientation == TreeOrientation.TopBottom || mOrientation == TreeOrientation.BottomTop); 245 double d = (v ? item.Rectangle.Height : item.Rectangle.Width); 246 if (m_depths.Length <= depth) 247 Array.Resize<float>(ref m_depths, (int)(3 * depth / 2)); 248 m_depths[depth] = (float)Math.Max(m_depths[depth], d); 249 mMaxDepth = (int)Math.Max(mMaxDepth, depth); 250 } 251 252 private void determineDepths() 253 { 254 for (int i = 1; i < mMaxDepth; ++i) 255 m_depths[i] += m_depths[i - 1] + mDepth; 256 } 257 258 public void Layout() 259 { 260 m_depths.Initialize(); 261 262 mMaxDepth = 0; 263 264 PointF a = LayoutAnchor; 265 m_ax = a.X; 266 m_ay = a.Y; 267 268 INode root = LayoutRoot as INode; 269 Params rp = Pars[root.Uid.ToString()]; 270 271 // do first pass - compute breadth information, collect depth info 272 firstWalk(root, 0, 1); 273 274 // sum up the depth info 275 determineDepths(); 276 277 // do second pass - assign layout positions 278 secondWalk(root, null, -rp.prelim, 0); 279 } 280 281 private void firstWalk(INode n, int num, int depth) 282 { 283 //Trace.WriteLine("depthj: " + depth); 284 Params np = Pars[n.Uid.ToString()]; 285 286 np.number = num; 287 updateDepths(depth, n); 288 289 bool expanded = n.IsExpanded; 290 if (n.ChildCount == 0 || !expanded) // is leaf 291 { 292 INode l = (INode)n.PreviousSibling; 293 if (l == null) 294 { 295 np.prelim = 0; 296 } 297 else 298 { 299 np.prelim = Pars[l.Uid.ToString()].prelim + spacing(l, n, true); 300 } 301 } 302 else if (expanded) 303 { 304 INode leftMost = n.FirstChild; 305 INode rightMost = n.LastChild; 306 INode defaultAncestor = leftMost; 307 INode c = leftMost; 308 for (int i = 0; c != null; ++i, c = c.NextSibling) 309 { 310 firstWalk(c, i, depth + 1); 311 defaultAncestor = apportion(c, defaultAncestor); 312 } 313 314 executeShifts(n); 315 316 double midpoint = 0.5 * 317 (Pars[leftMost.Uid.ToString()].prelim + Pars[rightMost.Uid.ToString()].prelim); 318 319 INode left = (INode)n.PreviousSibling; 320 if (left != null) 321 { 322 np.prelim = Pars[left.Uid.ToString()].prelim + spacing(left, n, true); 323 np.mod = np.prelim - midpoint; 324 } 325 else 326 { 327 np.prelim = midpoint; 328 } 329 } 330 } 331 private INode apportion(INode v, INode a) 332 { 333 INode w = (INode)v.PreviousSibling; 334 if (w != null) 335 { 336 INode vip, vim, vop, vom; 337 double sip, sim, sop, som; 338 339 vip = vop = v; 340 vim = w; 341 vom = (INode)vip.ParentNode.FirstChild; 342 343 sip = Pars[vip.Uid.ToString()].mod; 344 sop = Pars[vop.Uid.ToString()].mod; 345 sim = Pars[vim.Uid.ToString()].mod; 346 som = Pars[vom.Uid.ToString()].mod; 347 Params parms; 348 INode nr = nextRight(vim); 349 INode nl = nextLeft(vip); 350 while (nr != null && nl != null) 351 { 352 vim = nr; 353 vip = nl; 354 vom = nextLeft(vom); 355 vop = nextRight(vop); 356 parms = Pars[vop.Uid.ToString()]; 357 parms.ancestor = v; 358 double shift = (Pars[vim.Uid.ToString()].prelim + sim) - 359 (Pars[vip.Uid.ToString()].prelim + sip) + spacing(vim, vip, false); 360 if (shift > 0) 361 { 362 moveSubtree(ancestor(vim, v, a), v, shift); 363 sip += shift; 364 sop += shift; 365 } 366 sim += Pars[vim.Uid.ToString()].mod; 367 sip += Pars[vip.Uid.ToString()].mod; 368 som += Pars[vom.Uid.ToString()].mod; 369 sop += Pars[vop.Uid.ToString()].mod; 370 371 nr = nextRight(vim); 372 nl = nextLeft(vip); 373 } 374 if (nr != null && nextRight(vop) == null) 375 { 376 Params vopp = Pars[vop.Uid.ToString()]; 377 vopp.thread = nr; 378 vopp.mod += sim - sop; 379 } 380 if (nl != null && nextLeft(vom) == null) 381 { 382 Params vomp = Pars[vom.Uid.ToString()]; 383 vomp.thread = nl; 384 vomp.mod += sip - som; 385 a = v; 386 } 387 } 388 return a; 389 } 390 private INode nextLeft(INode n) 391 { 392 INode c = null; 393 if (n.IsExpanded) c = n.FirstChild; 394 return (c != null ? c : Pars[n.Uid.ToString()].thread); 395 } 396 397 private INode nextRight(INode n) 398 { 399 INode c = null; 400 if (n.IsExpanded) c = n.LastChild; 401 return (c != null ? c : Pars[n.Uid.ToString()].thread); 402 } 403 404 private void moveSubtree(INode wm, INode wp, double shift) 405 { 406 Params wmp = Pars[wm.Uid.ToString()]; 407 Params wpp = Pars[wp.Uid.ToString()]; 408 double subtrees = wpp.number - wmp.number; 409 wpp.change -= shift / subtrees; 410 wpp.shift += shift; 411 wmp.change += shift / subtrees; 412 wpp.prelim += shift; 413 wpp.mod += shift; 414 } 415 416 private void executeShifts(INode n) 417 { 418 double shift = 0, change = 0; 419 for (INode c = n.LastChild; c != null; c = c.PreviousSibling) 420 { 421 Params cp = Pars[c.Uid.ToString()]; 422 cp.prelim += shift; 423 cp.mod += shift; 424 change += cp.change; 425 shift += cp.shift + change; 426 } 427 } 428 429 private INode ancestor(INode vim, INode v, INode a) 430 { 431 INode p = (INode)v.ParentNode; 432 Params vimp = Pars[vim.Uid.ToString()]; 433 if (vimp.ancestor != null && vimp.ancestor.ParentNode == p) 434 { 435 return vimp.ancestor; 436 } 437 else 438 { 439 return a; 440 } 441 } 442 443 private void secondWalk(INode n, INode p, double m, int depth) 444 { 445 Params np = Pars[n.Uid.ToString()]; 446 setBreadth(n, p, np.prelim + m); 447 setDepth(n, p, m_depths[depth]); 448 449 if (n.IsExpanded) 450 { 451 depth += 1; 452 for (INode c = n.FirstChild; c != null; c = c.NextSibling) 453 { 454 if (worker.CancellationPending) 455 break; 456 secondWalk(c, n, m + np.mod, depth); 457 } 458 } 459 460 np.clear(); 461 } 462 463 private void setBreadth(INode n, INode p, double b) 464 { 465 switch (mOrientation) 466 { 467 case TreeOrientation.LeftRight: 468 case TreeOrientation.RightLeft: 469 setY(n, p, m_ay + b); 470 break; 471 case TreeOrientation.TopBottom: 472 case TreeOrientation.BottomTop: 473 setX(n, p, m_ax + b); 474 break; 475 default: 476 throw new InconsistencyException(); 477 } 478 } 479 480 private void setDepth(INode n, INode p, double d) 481 { 482 switch (mOrientation) 483 { 484 case TreeOrientation.LeftRight: 485 setX(n, p, m_ax + d); 486 break; 487 case TreeOrientation.RightLeft: 488 setX(n, p, m_ax - d); 489 break; 490 case TreeOrientation.TopBottom: 491 setY(n, p, m_ay + d); 492 break; 493 case TreeOrientation.BottomTop: 494 setY(n, p, m_ay - d); 495 break; 496 default: 497 throw new InconsistencyException(); 498 } 499 } 500 501 502 #endregion 503 504 /// <summary> 505 /// Paramter blob to temporarily keep working data of one node. 506 /// </summary> 507 class Params 508 { 509 public double prelim; 510 public double mod; 511 public double shift; 512 public double change; 513 public int number; 514 public INode ancestor; 515 public INode thread; 516 517 public void init(INode item) 518 { 519 ancestor = item; 520 number = -1; 521 ancestor = thread = null; 522 } 523 524 public void clear() 525 { 526 number = -2; 527 prelim = mod = shift = change = 0; 528 ancestor = thread = null; 529 } 530 } 531 } 532 533 public enum TreeOrientation 534 { 535 LeftRight, 536 RightLeft, 537 TopBottom, 538 BottomTop, 539 Center 540 } 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 } 541 476 }
Note: See TracChangeset
for help on using the changeset viewer.