LayeredTreeDraw.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. #define FIX
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Windows;
  6. namespace BehaviorTree
  7. {
  8. public enum VerticalJustification
  9. {
  10. TOP,
  11. CENTER,
  12. BOTTOM
  13. }
  14. public class LayeredTreeDraw
  15. {
  16. ITreeNode tnRoot;
  17. double pxBufferHorizontal;
  18. double pxBufferHorizontalSubtree;
  19. double pxBufferVertical;
  20. List<TreeConnection> lsttcn = new List<TreeConnection>();
  21. List<double> lstLayerHeight = new List<double>();
  22. VerticalJustification vj;
  23. static TreeNodeGroup tngEmpty = new TreeNodeGroup();
  24. public double PxOverallHeight
  25. {
  26. get;
  27. private set;
  28. }
  29. public double PxOverallWidth
  30. {
  31. get
  32. {
  33. return Info(tnRoot).SubTreeWidth;
  34. }
  35. }
  36. public List<TreeConnection> Connections
  37. {
  38. get
  39. {
  40. return lsttcn;
  41. }
  42. }
  43. public LayeredTreeDraw(
  44. ITreeNode tnRoot, double pxBufferHorizontal,
  45. double pxBufferHorizontalSubtree, double pxBufferVertical,
  46. VerticalJustification vj)
  47. {
  48. this.pxBufferHorizontal = pxBufferHorizontal;
  49. this.pxBufferHorizontalSubtree = pxBufferHorizontalSubtree;
  50. this.pxBufferVertical = pxBufferVertical;
  51. PxOverallHeight = 0.0;
  52. this.tnRoot = tnRoot;
  53. this.vj = vj;
  54. }
  55. private static LayeredTreeInfo Info(ITreeNode ign)
  56. {
  57. return (LayeredTreeInfo)ign.PrivateNodeInfo;
  58. }
  59. public double X(ITreeNode tn)
  60. {
  61. if (Info(tn) == null)
  62. {
  63. return 0;
  64. }
  65. return Info(tn).pxFromLeft;
  66. }
  67. public double Y(ITreeNode tn)
  68. {
  69. if (Info(tn) == null)
  70. {
  71. return 0;
  72. }
  73. return Info(tn).pxFromTop;
  74. }
  75. static public IEnumerable<T> VisibleDescendants<T>(ITreeNode tn)
  76. {
  77. foreach (ITreeNode tnCur in tn.TreeChildren)
  78. {
  79. if (!tnCur.Collapsed)
  80. {
  81. foreach (T item in VisibleDescendants<T>(tnCur))
  82. {
  83. yield return item;
  84. }
  85. }
  86. yield return (T)tnCur;
  87. }
  88. }
  89. static public IEnumerable<T> Descendants<T>(ITreeNode tn)
  90. {
  91. foreach (ITreeNode tnCur in tn.TreeChildren)
  92. {
  93. foreach (T item in Descendants<T>(tnCur))
  94. {
  95. yield return item;
  96. }
  97. yield return (T)tnCur;
  98. }
  99. }
  100. public void LayoutTree()
  101. {
  102. LayoutTree(tnRoot, 0);
  103. DetermineFinalPositions(tnRoot, 0, 0, Info(tnRoot).pxLeftPosRelativeToBoundingBox);
  104. }
  105. private void LayoutTree(ITreeNode tnRoot, int iLayer)
  106. {
  107. if (GetChildren(tnRoot).Count == 0)
  108. {
  109. LayoutLeafNode(tnRoot);
  110. }
  111. else
  112. {
  113. LayoutInteriorNode(tnRoot, iLayer);
  114. }
  115. UpdateLayerHeight(tnRoot, iLayer);
  116. }
  117. private static void LayoutLeafNode(ITreeNode tnRoot)
  118. {
  119. double width = tnRoot.TreeWidth;
  120. LayeredTreeInfo lti = new LayeredTreeInfo(width, tnRoot);
  121. lti.lstPosLeftBoundaryRelativeToRoot.Add(0);
  122. lti.lstPosRightBoundaryRelativeToRoot.Add(width);
  123. tnRoot.PrivateNodeInfo = lti;
  124. }
  125. private void LayoutInteriorNode(ITreeNode tnRoot, int iLayer)
  126. {
  127. ITreeNode tnLast = null;
  128. TreeNodeGroup tng = GetChildren(tnRoot);
  129. ITreeNode itn = tng[0];
  130. LayeredTreeInfo ltiThis;
  131. LayoutAllOurChildren(iLayer, tnLast, tng);
  132. // This width doesn't account for the parent node's width...
  133. ltiThis = new LayeredTreeInfo(CalculateWidthFromInterChildDistances(tnRoot), tnRoot);
  134. tnRoot.PrivateNodeInfo = ltiThis;
  135. // ...so that this centering may place the parent node negatively while the "width" is the width of
  136. // all the child nodes.
  137. CenterOverChildren(tnRoot, ltiThis);
  138. DetermineParentRelativePositionsOfChildren(tnRoot);
  139. CalculateBoundaryLists(tnRoot);
  140. }
  141. private void LayoutAllOurChildren(int iLayer, ITreeNode tnLast, TreeNodeGroup tng)
  142. {
  143. List<Double> lstLeftToBB = new List<double>();
  144. List<int> lstResponsible = new List<int>();
  145. for (int i = 0; i < tng.Count; i++)
  146. {
  147. ITreeNode tn = tng[i];
  148. LayoutTree(tn, iLayer + 1);
  149. RepositionSubtree(i, tng, lstLeftToBB, lstResponsible);
  150. tnLast = tn;
  151. }
  152. }
  153. private static void CenterOverChildren(ITreeNode tnRoot, LayeredTreeInfo ltiThis)
  154. {
  155. // We should be centered between the connection points of our children...
  156. ITreeNode tnLeftMost = tnRoot.TreeChildren.LeftMost();
  157. double pxLeftChild = Info(tnLeftMost).pxLeftPosRelativeToBoundingBox + tnLeftMost.TreeWidth / 2;
  158. ITreeNode tnRightMost = tnRoot.TreeChildren.RightMost();
  159. double pxRightChild = Info(tnRightMost).pxLeftPosRelativeToBoundingBox + tnRightMost.TreeWidth / 2;
  160. ltiThis.pxLeftPosRelativeToBoundingBox = (pxLeftChild + pxRightChild - tnRoot.TreeWidth) / 2;
  161. // If the root node was wider than the subtree, then we'll have a negative position for it. We need
  162. // to readjust things so that the left of the root node represents the left of the bounding box and
  163. // the child distances to the Bounding box need to be adjusted accordingly.
  164. if (ltiThis.pxLeftPosRelativeToBoundingBox < 0)
  165. {
  166. foreach (ITreeNode tnChildCur in tnRoot.TreeChildren)
  167. {
  168. Info(tnChildCur).pxLeftPosRelativeToBoundingBox -= ltiThis.pxLeftPosRelativeToBoundingBox;
  169. }
  170. ltiThis.pxLeftPosRelativeToBoundingBox = 0;
  171. }
  172. }
  173. private void DetermineParentRelativePositionsOfChildren(ITreeNode tnRoot)
  174. {
  175. LayeredTreeInfo ltiRoot = Info(tnRoot);
  176. foreach (ITreeNode tn in GetChildren(tnRoot))
  177. {
  178. LayeredTreeInfo ltiCur = Info(tn);
  179. ltiCur.pxLeftPosRelativeToParent = ltiCur.pxLeftPosRelativeToBoundingBox - ltiRoot.pxLeftPosRelativeToBoundingBox;
  180. }
  181. }
  182. private double CalculateWidthFromInterChildDistances(ITreeNode tnRoot)
  183. {
  184. double pxWidthCur;
  185. LayeredTreeInfo lti;
  186. double pxWidth = 0.0;
  187. lti = Info(tnRoot.TreeChildren.LeftMost());
  188. pxWidthCur = lti.pxLeftPosRelativeToBoundingBox;
  189. // If a subtree extends deeper than it's left neighbors then at that lower level it could potentially extend beyond those neighbors
  190. // on the left. We have to check for this and make adjustements after the loop if it occurred.
  191. double pxUndercut = 0.0;
  192. foreach (ITreeNode tn in tnRoot.TreeChildren)
  193. {
  194. lti = Info(tn);
  195. pxWidthCur += lti.pxToLeftSibling;
  196. if (lti.pxLeftPosRelativeToBoundingBox > pxWidthCur)
  197. {
  198. pxUndercut = Math.Max(pxUndercut, lti.pxLeftPosRelativeToBoundingBox - pxWidthCur);
  199. }
  200. // pxWidth might already be wider than the current node's subtree if earlier nodes "undercut" on the
  201. // right hand side so we have to take the Max here...
  202. pxWidth = Math.Max(pxWidth, pxWidthCur + lti.SubTreeWidth - lti.pxLeftPosRelativeToBoundingBox);
  203. // After this next statement, the BoundingBox we're relative to is the one of our parent's subtree rather than
  204. // our own subtree (with the exception of undercut considerations)
  205. lti.pxLeftPosRelativeToBoundingBox = pxWidthCur;
  206. }
  207. if (pxUndercut > 0.0)
  208. {
  209. foreach (ITreeNode tn in tnRoot.TreeChildren)
  210. {
  211. Info(tn).pxLeftPosRelativeToBoundingBox += pxUndercut;
  212. }
  213. pxWidth += pxUndercut;
  214. }
  215. // We are never narrower than our root node's width which we haven't taken into account yet so
  216. // we do that here.
  217. return Math.Max(tnRoot.TreeWidth, pxWidth);
  218. }
  219. private void CalculateBoundaryLists(ITreeNode tnRoot)
  220. {
  221. LayeredTreeInfo lti = Info(tnRoot);
  222. lti.lstPosLeftBoundaryRelativeToRoot.Add(0.0);
  223. lti.lstPosRightBoundaryRelativeToRoot.Add(tnRoot.TreeWidth);
  224. DetermineBoundary(tnRoot.TreeChildren, true /* fLeft */, lti.lstPosLeftBoundaryRelativeToRoot);
  225. DetermineBoundary(tnRoot.TreeChildren.Reverse(), false /* fLeft */, lti.lstPosRightBoundaryRelativeToRoot);
  226. }
  227. private void DetermineBoundary(IEnumerable<ITreeNode> entn, bool fLeft, List<double> lstPos)
  228. {
  229. int cLayersDeep = 1;
  230. List<double> lstPosCur;
  231. foreach (ITreeNode tnChild in entn)
  232. {
  233. LayeredTreeInfo ltiChild = Info(tnChild);
  234. if (fLeft)
  235. {
  236. lstPosCur = ltiChild.lstPosLeftBoundaryRelativeToRoot;
  237. }
  238. else
  239. {
  240. lstPosCur = ltiChild.lstPosRightBoundaryRelativeToRoot;
  241. }
  242. if (lstPosCur.Count >= lstPos.Count)
  243. {
  244. using (IEnumerator<double> enPosCur = lstPosCur.GetEnumerator())
  245. {
  246. for (int i = 0; i < cLayersDeep - 1; i++)
  247. {
  248. enPosCur.MoveNext();
  249. }
  250. while (enPosCur.MoveNext())
  251. {
  252. lstPos.Add(enPosCur.Current + ltiChild.pxLeftPosRelativeToParent);
  253. cLayersDeep++;
  254. }
  255. }
  256. }
  257. }
  258. }
  259. private void ApportionSlop(int itn, int itnResponsible, TreeNodeGroup tngSiblings)
  260. {
  261. LayeredTreeInfo lti = Info(tngSiblings[itn]);
  262. ITreeNode tnLeft = tngSiblings[itn - 1];
  263. double pxSlop = lti.pxToLeftSibling - tnLeft.TreeWidth - pxBufferHorizontal;
  264. if (pxSlop > 0)
  265. {
  266. for (int i = itnResponsible + 1; i < itn; i++)
  267. {
  268. Info(tngSiblings[i]).pxToLeftSibling += pxSlop * (i - itnResponsible) / (itn - itnResponsible);
  269. }
  270. lti.pxToLeftSibling -= (itn - itnResponsible - 1) * pxSlop / (itn - itnResponsible);
  271. }
  272. }
  273. private void RepositionSubtree(
  274. int itn, TreeNodeGroup tngSiblings,
  275. List<double> lstLeftToBB, List<int> lsttnResponsible)
  276. {
  277. int itnResponsible;
  278. ITreeNode tn = tngSiblings[itn];
  279. LayeredTreeInfo lti = Info(tn);
  280. if (itn == 0)
  281. {
  282. // No shifting but we still have to prepare the initial version of the
  283. // left hand skeleton list
  284. foreach (double pxRelativeToRoot in lti.lstPosRightBoundaryRelativeToRoot)
  285. {
  286. lstLeftToBB.Add(pxRelativeToRoot + lti.pxLeftPosRelativeToBoundingBox);
  287. lsttnResponsible.Add(0);
  288. }
  289. return;
  290. }
  291. ITreeNode tnLeft = tngSiblings[itn - 1];
  292. LayeredTreeInfo ltiLeft = Info(tnLeft);
  293. int iLayer;
  294. double pxHorizontalBuffer = pxBufferHorizontal;
  295. double pxNewPosFromBB = PxCalculateNewPos(lti, lstLeftToBB, lsttnResponsible, out itnResponsible, out iLayer);
  296. if (iLayer != 0)
  297. {
  298. pxHorizontalBuffer = pxBufferHorizontalSubtree;
  299. }
  300. lti.pxToLeftSibling = pxNewPosFromBB - lstLeftToBB.First() + tnLeft.TreeWidth + pxHorizontalBuffer;
  301. int cLevels = Math.Min(lti.lstPosRightBoundaryRelativeToRoot.Count, lstLeftToBB.Count);
  302. for (int i = 0; i < cLevels; i++)
  303. {
  304. lstLeftToBB[i] = lti.lstPosRightBoundaryRelativeToRoot[i] + pxNewPosFromBB + pxHorizontalBuffer;
  305. lsttnResponsible[i] = itn;
  306. }
  307. for (int i = lstLeftToBB.Count; i < lti.lstPosRightBoundaryRelativeToRoot.Count; i++)
  308. {
  309. lstLeftToBB.Add(lti.lstPosRightBoundaryRelativeToRoot[i] + pxNewPosFromBB + pxHorizontalBuffer);
  310. lsttnResponsible.Add(itn);
  311. }
  312. ApportionSlop(itn, itnResponsible, tngSiblings);
  313. }
  314. private double PxCalculateNewPos(
  315. LayeredTreeInfo lti, List<double> lstLeftToBB,
  316. List<int> lstitnResponsible, out int itnResponsible,
  317. out int iLayerRet)
  318. {
  319. double pxOffsetToBB = lstLeftToBB[0];
  320. int cLayers = Math.Min(lti.lstPosLeftBoundaryRelativeToRoot.Count, lstLeftToBB.Count);
  321. double pxRootPosRightmost = 0.0;
  322. iLayerRet = 0;
  323. using (IEnumerator<double> enRight = lti.lstPosLeftBoundaryRelativeToRoot.GetEnumerator(),
  324. enLeft = lstLeftToBB.GetEnumerator())
  325. using (IEnumerator<int> enResponsible = lstitnResponsible.GetEnumerator())
  326. {
  327. itnResponsible = -1;
  328. enRight.MoveNext();
  329. enLeft.MoveNext();
  330. enResponsible.MoveNext();
  331. for (int iLayer = 0; iLayer < cLayers; iLayer++)
  332. {
  333. double pxLeftBorderFromBB = enLeft.Current;
  334. double pxRightBorderFromRoot = enRight.Current;
  335. double pxRightRootBasedOnThisLevel;
  336. int itnResponsibleCur = enResponsible.Current;
  337. enLeft.MoveNext();
  338. enRight.MoveNext();
  339. enResponsible.MoveNext();
  340. pxRightRootBasedOnThisLevel = pxLeftBorderFromBB - pxRightBorderFromRoot;
  341. if (pxRightRootBasedOnThisLevel > pxRootPosRightmost)
  342. {
  343. iLayerRet = iLayer;
  344. pxRootPosRightmost = pxRightRootBasedOnThisLevel;
  345. itnResponsible = itnResponsibleCur;
  346. }
  347. }
  348. }
  349. return pxRootPosRightmost;
  350. }
  351. private void UpdateLayerHeight(ITreeNode tnRoot, int iLayer)
  352. {
  353. while (lstLayerHeight.Count <= iLayer)
  354. {
  355. lstLayerHeight.Add(0.0);
  356. }
  357. lstLayerHeight[iLayer] = Math.Max(tnRoot.TreeHeight, lstLayerHeight[iLayer]);
  358. }
  359. private System.Double CalcJustify(double height, double pxRowHeight)
  360. {
  361. double dRet = 0.0;
  362. switch (vj)
  363. {
  364. case VerticalJustification.TOP:
  365. break;
  366. case VerticalJustification.CENTER:
  367. dRet = (pxRowHeight - height) / 2;
  368. break;
  369. case VerticalJustification.BOTTOM:
  370. dRet = pxRowHeight - height;
  371. break;
  372. }
  373. return dRet;
  374. }
  375. private TreeNodeGroup GetChildren(ITreeNode tn)
  376. {
  377. if (tn.Collapsed)
  378. {
  379. return tngEmpty;
  380. }
  381. return tn.TreeChildren;
  382. }
  383. private void DetermineFinalPositions(ITreeNode tn, int iLayer, double pxFromTop, double pxParentFromLeft)
  384. {
  385. double pxRowHeight = lstLayerHeight[iLayer];
  386. LayeredTreeInfo lti = Info(tn);
  387. double pxBottom;
  388. Point dptOrigin;
  389. lti.pxFromTop = pxFromTop + CalcJustify(tn.TreeHeight, pxRowHeight);
  390. pxBottom = lti.pxFromTop + tn.TreeHeight;
  391. if (pxBottom > PxOverallHeight)
  392. {
  393. PxOverallHeight = pxBottom;
  394. }
  395. lti.pxFromLeft = lti.pxLeftPosRelativeToParent + pxParentFromLeft;
  396. dptOrigin = new Point(lti.pxFromLeft + tn.TreeWidth / 2, lti.pxFromTop + tn.TreeHeight);
  397. iLayer++;
  398. foreach (ITreeNode tnCur in GetChildren(tn))
  399. {
  400. List<Point> lstcpt = new List<Point>();
  401. LayeredTreeInfo ltiCur = Info(tnCur);
  402. lstcpt.Add(dptOrigin);
  403. DetermineFinalPositions(tnCur, iLayer, pxFromTop + pxRowHeight + pxBufferVertical, lti.pxFromLeft);
  404. lstcpt.Add(new Point(ltiCur.pxFromLeft + tnCur.TreeWidth / 2, ltiCur.pxFromTop));
  405. lsttcn.Add(new TreeConnection(tn, tnCur, lstcpt));
  406. }
  407. }
  408. private class LayeredTreeInfo
  409. {
  410. public double SubTreeWidth { get; set; }
  411. public double pxLeftPosRelativeToParent { get; set; }
  412. public double pxLeftPosRelativeToBoundingBox { get; set; }
  413. public double pxToLeftSibling { get; set; }
  414. public double pxFromTop { get; set; }
  415. public double pxFromLeft { get; set; }
  416. public ITreeNode ign { get; private set; }
  417. public List<double> lstPosLeftBoundaryRelativeToRoot = new List<double>();
  418. public List<double> lstPosRightBoundaryRelativeToRoot = new List<double>();
  419. /// <summary>
  420. /// Initializes a new instance of the GraphLayoutInfo class.
  421. /// </summary>
  422. public LayeredTreeInfo(double subTreeWidth, ITreeNode tn)
  423. {
  424. SubTreeWidth = subTreeWidth;
  425. pxLeftPosRelativeToParent = 0;
  426. pxFromTop = 0;
  427. ign = tn;
  428. }
  429. }
  430. }
  431. }