!B (Sandbox) (CE-21795) Importing models with multisubmaterials via fbx switches...
[CRYENGINE.git] / Code / Tools / Statoscope / Statoscope / TreemapControl.cs
bloba2a3904405c0f4fe7abfd216c763637c70f062ee
1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Text;
7 using CsGL.OpenGL;
8 using System.Drawing;
9 using System.Windows.Forms;
11 namespace Statoscope
13 class TreemapNode
15 public double Size
17 get { return m_size; }
18 set { m_size = value; }
21 public object Tag
23 get { return m_tag; }
24 set { m_tag = value; }
27 public string Name
29 get { return m_name; }
30 set { m_name = value; }
33 public GLTexture Texture
35 get { return m_texture; }
36 set { m_texture = value; }
39 internal RectD Bounds;
40 internal List<TreemapNode> Children = new List<TreemapNode>();
42 public TreemapNode()
46 private double m_size;
47 private object m_tag;
48 private string m_name = string.Empty;
49 private GLTexture m_texture = null;
52 class TreemapControl : CanvasControl
54 private double m_pxNodeMargin = 1.5;
56 private ToolTip m_tooltip;
58 private TreemapNode m_hoverNode;
59 private TreemapNode m_selectNode;
60 private Point m_lastPoint;
62 public TreemapNode Tree
64 get { return m_tree; }
67 public TreemapNode SelectedNode
69 get { return m_selectNode; }
72 public double NodeMarginPx
74 get { return m_pxNodeMargin; }
75 set { m_pxNodeMargin = value; }
78 public EventHandler SelectionChanged;
80 public TreemapControl()
82 m_tooltip = new ToolTip();
83 m_tooltip.AutoPopDelay = 5000;
84 m_tooltip.InitialDelay = 1000;
85 m_tooltip.ReshowDelay = 500;
88 public TreemapNode CreateTree()
90 m_tree = new TreemapNode();
91 m_tree.Bounds = new RectD(0, 0, 1, 1);
92 m_dirty = true;
93 m_selectNode = null;
94 VirtualBounds = m_tree.Bounds;
95 return m_tree;
98 public TreemapNode CreateChild(TreemapNode parent)
100 TreemapNode n = new TreemapNode();
101 parent.Children.Add(n);
102 m_dirty = true;
103 return n;
106 public void InvalidateLayout()
108 m_dirty = true;
109 Invalidate();
112 private TreemapNode HitTest(TreemapNode n, double x, double y)
114 if (x >= n.Bounds.Left && x < n.Bounds.Right)
116 if (y >= n.Bounds.Top && y < n.Bounds.Bottom)
118 foreach (TreemapNode c in n.Children)
120 TreemapNode hn = HitTest(c, x, y);
121 if (hn != null)
122 return hn;
125 return n;
129 return null;
132 public TreemapNode HitTest(Point clientPt)
134 double smpX = SampleXFromClientX(clientPt.X);
135 double smpY = SampleYFromClientY(clientPt.Y);
137 if (m_tree != null)
139 TreemapNode hit = null;
140 EnumTree(
141 m_tree, new RectD(0, 0, 1, 1), smpX, smpY, 0,
142 (TreemapNode n, RectD nBounds, int depth) =>
144 if (smpX < nBounds.Left) return false;
145 if (smpX >= nBounds.Right) return false;
146 if (smpY < nBounds.Top) return false;
147 if (smpY >= nBounds.Bottom) return false;
149 hit = n;
151 return true;
153 return hit;
156 return null;
159 protected override void glDraw()
161 if (m_tree != null)
163 InitResources();
165 ResetViewport();
166 PrepareViewport();
168 if (m_dirty)
170 LayoutTree(m_tree);
171 m_dirty = false;
174 RectD vBounds = View;
176 double pxUnitsX = GetSamplesPerPixelX();
177 double pxUnitsY = GetSamplesPerPixelY();
179 OpenGL.glBegin(OpenGL.GL_QUADS);
180 FillNode(m_tree, new RectD(0, 0, 1, 1), vBounds, pxUnitsX, pxUnitsY, 0);
181 OpenGL.glEnd();
183 float windowAspect = ClientSize.Width / (float)ClientSize.Height;
184 float viewportAspect = (float)(View.Width / View.Height);
186 EnumTree(
187 m_tree, new RectD(0, 0, 1, 1), pxUnitsX, pxUnitsY, 0,
188 (TreemapNode n, RectD nBounds, int depth) =>
190 if (Overlaps(nBounds, vBounds))
192 RectD titleBounds = new RectD(
193 nBounds.Left,
194 nBounds.Top,
195 nBounds.Right,
196 nBounds.Top + nBounds.Height * 0.03f);
197 if (titleBounds.Height > pxUnitsY * 3)
199 float yTextScale = (float)(titleBounds.Height / m_titleFont.Height);
200 float xTextScale = yTextScale / (windowAspect / viewportAspect);
201 m_titleFont.Draw((float)titleBounds.Left, (float)titleBounds.Top, Color.Black, n.Name, xTextScale, yTextScale);
203 return true;
206 return false;
209 OpenGL.glBegin(OpenGL.GL_LINES);
210 StrokeNode(m_tree, new RectD(0, 0, 1, 1), vBounds, pxUnitsX, pxUnitsY, 0);
211 OpenGL.glEnd();
215 protected virtual string FormatTooltip(TreemapNode node)
217 return node.Name;
220 protected override void OnMouseMove(MouseEventArgs e)
222 if (e.Button == MouseButtons.None)
224 if (e.X != m_lastPoint.X || e.Y != m_lastPoint.Y)
226 TreemapNode selectedNode = HitTest(e.Location);
228 if (selectedNode != m_hoverNode)
230 string tt = string.Empty;
232 m_hoverNode = selectedNode;
233 if (selectedNode != null)
234 tt = FormatTooltip(selectedNode);
236 m_tooltip.SetToolTip(this, tt);
240 m_lastPoint = new Point(e.X, e.Y);
243 base.OnMouseMove(e);
246 protected virtual void OnSelectedNode()
248 if (SelectionChanged != null)
249 SelectionChanged(this, EventArgs.Empty);
252 protected override void OnLClick(MouseEventArgs e)
254 base.OnLClick(e);
256 TreemapNode node = HitTest(e.Location);
257 m_selectNode = node;
258 if (m_selectNode != null)
259 OnSelectedNode();
262 private void InitResources()
264 if (m_titleFont == null)
266 Font font = new Font(FontFamily.GenericSansSerif, 64.0f, GraphicsUnit.Pixel);
267 m_titleFont = new GLFont(font);
271 private delegate bool EnumTreeHandler(TreemapNode n, RectD nBounds, int depth);
273 private void EnumTree(TreemapNode n, RectD nBounds, double pxX, double pxY, int depth, EnumTreeHandler h)
275 if (h(n, nBounds, depth))
277 RectD conBounds = new RectD(
278 nBounds.Left + nBounds.Width * 0.01f,
279 nBounds.Top + nBounds.Height * 0.03f,
280 nBounds.Right - nBounds.Width * 0.01f,
281 nBounds.Bottom - nBounds.Height * 0.01f);
282 for (int i = 0, c = n.Children.Count; i != c; ++i)
284 TreemapNode cn = n.Children[i];
286 RectD cBounds = new RectD(
287 cn.Bounds.Left * conBounds.Width + conBounds.Left,
288 cn.Bounds.Top * conBounds.Height + conBounds.Top,
289 cn.Bounds.Right * conBounds.Width + conBounds.Left,
290 cn.Bounds.Bottom * conBounds.Height + conBounds.Top);
292 if (cBounds.Width > pxX * 5)
294 cBounds.Left += pxX * m_pxNodeMargin;
295 cBounds.Right -= pxX * m_pxNodeMargin;
298 if (cBounds.Height > pxY * 5)
300 cBounds.Top += pxY * m_pxNodeMargin;
301 cBounds.Bottom -= pxY * m_pxNodeMargin;
304 EnumTree(cn, cBounds, pxX, pxY, depth + 1, h);
309 private static bool Overlaps(RectD a, RectD b)
311 if (a.Right <= b.Left) return false;
312 if (b.Right <= a.Left) return false;
313 if (a.Bottom <= b.Top) return false;
314 if (b.Bottom <= a.Top) return false;
315 return true;
318 private void FillNode(TreemapNode n, RectD nBounds, RectD vBounds, double pxX, double pxY, int depth)
320 if (Overlaps(nBounds, vBounds))
322 GLTexture tex = n.Texture;
323 if (tex != null && tex.Valid)
325 OpenGL.glEnd();
326 OpenGL.glEnable(OpenGL.GL_TEXTURE_2D);
327 OpenGL.glDisable(OpenGL.GL_BLEND);
328 tex.Bind();
330 OpenGL.glColor3f(1.0f, 1.0f, 1.0f);
331 OpenGL.glBegin(OpenGL.GL_QUADS);
333 else
335 RGB col = new RGB(new HSV(depth * (1.0f / 6.0f), 0.5f - depth / 256.0f, 1));
336 OpenGL.glColor3f(col.r, col.g, col.b);
340 OpenGL.glTexCoord2f(0.0f, 0.0f);
341 OpenGL.glVertex2f((float)nBounds.Left, (float)nBounds.Top);
343 OpenGL.glTexCoord2f(0.0f, 1.0f);
344 OpenGL.glVertex2f((float)nBounds.Left, (float)nBounds.Bottom);
346 OpenGL.glTexCoord2f(1.0f, 1.0f);
347 OpenGL.glVertex2f((float)nBounds.Right, (float)nBounds.Bottom);
349 OpenGL.glTexCoord2f(1.0f, 0.0f);
350 OpenGL.glVertex2f((float)nBounds.Right, (float)nBounds.Top);
353 if (tex != null && tex.Valid)
355 OpenGL.glEnd();
356 tex.Unbind();
357 OpenGL.glDisable(OpenGL.GL_TEXTURE_2D);
358 OpenGL.glEnable(OpenGL.GL_BLEND);
359 OpenGL.glBegin(OpenGL.GL_QUADS);
362 RectD conBounds = new RectD(
363 nBounds.Left + nBounds.Width * 0.01f,
364 nBounds.Top + nBounds.Height * 0.03f,
365 nBounds.Right - nBounds.Width * 0.01f,
366 nBounds.Bottom - nBounds.Height * 0.01f);
368 for (int i = 0, c = n.Children.Count; i != c; ++i)
370 TreemapNode cn = n.Children[i];
372 RectD cBounds = new RectD(
373 cn.Bounds.Left * conBounds.Width + conBounds.Left,
374 cn.Bounds.Top * conBounds.Height + conBounds.Top,
375 cn.Bounds.Right * conBounds.Width + conBounds.Left,
376 cn.Bounds.Bottom * conBounds.Height + conBounds.Top);
378 if (cBounds.Width > pxX * 5)
380 cBounds.Left += pxX * m_pxNodeMargin;
381 cBounds.Right -= pxX * m_pxNodeMargin;
384 if (cBounds.Height > pxY * 5)
386 cBounds.Top += pxY * m_pxNodeMargin;
387 cBounds.Bottom -= pxY * m_pxNodeMargin;
390 FillNode(cn, cBounds, vBounds, pxX, pxY, depth + 1);
395 private void StrokeNode(TreemapNode n, RectD nBounds, RectD vBounds, double pxX, double pxY, int depth)
397 if (Overlaps(nBounds, vBounds))
399 OpenGL.glColor3f(0, 0, 0);
402 OpenGL.glVertex2f((float)nBounds.Left, (float)nBounds.Top);
403 OpenGL.glVertex2f((float)nBounds.Left, (float)nBounds.Bottom);
405 OpenGL.glVertex2f((float)nBounds.Left, (float)nBounds.Bottom);
406 OpenGL.glVertex2f((float)nBounds.Right, (float)nBounds.Bottom);
408 OpenGL.glVertex2f((float)nBounds.Right, (float)nBounds.Bottom);
409 OpenGL.glVertex2f((float)nBounds.Right, (float)nBounds.Top);
411 OpenGL.glVertex2f((float)nBounds.Right, (float)nBounds.Top);
412 OpenGL.glVertex2f((float)nBounds.Left, (float)nBounds.Top);
415 RectD conBounds = new RectD(
416 nBounds.Left + nBounds.Width * 0.01f,
417 nBounds.Top + nBounds.Height * 0.03f,
418 nBounds.Right - nBounds.Width * 0.01f,
419 nBounds.Bottom - nBounds.Height * 0.01f);
421 for (int i = 0, c = n.Children.Count; i != c; ++i)
423 TreemapNode cn = n.Children[i];
425 RectD cBounds = new RectD(
426 cn.Bounds.Left * conBounds.Width + conBounds.Left,
427 cn.Bounds.Top * conBounds.Height + conBounds.Top,
428 cn.Bounds.Right * conBounds.Width + conBounds.Left,
429 cn.Bounds.Bottom * conBounds.Height + conBounds.Top);
431 if (cBounds.Width > pxX * 5)
433 cBounds.Left += pxX * m_pxNodeMargin;
434 cBounds.Right -= pxX * m_pxNodeMargin;
437 if (cBounds.Height > pxY * 5)
439 cBounds.Top += pxY * m_pxNodeMargin;
440 cBounds.Bottom -= pxY * m_pxNodeMargin;
443 StrokeNode(cn, cBounds, vBounds, pxX, pxY, depth + 1);
448 static private bool CompareRatio(List<TreemapNode> row, TreemapNode next, double w)
450 double mn = float.MaxValue;
451 double sum = 0.0f;
452 double mx = 0.0f;
454 for (int i = 0, c = row.Count; i != c; ++ i)
456 TreemapNode n = row[i];
457 mx = Math.Max(mx, n.Size);
458 mn = Math.Min(mn, n.Size);
459 sum += n.Size;
462 double sumSq = sum * sum;
463 double wSq = w * w;
464 double ratio = Math.Max((wSq * mx) / sumSq, sumSq / (wSq * mn));
466 double nextMx = Math.Max(mx, next.Size);
467 double nextMn = Math.Min(mn, next.Size);
468 double nextSum = sum + next.Size;
470 double nextSumSq = nextSum * nextSum;
471 double nextRatio = Math.Max((wSq * nextMx) / nextSumSq, nextSumSq / (wSq * nextMn));
473 return ratio <= nextRatio;
476 static private void LayoutRow(ref RectD bounds, List<TreemapNode> nodes, out RectD remainder)
478 double sum = 0.0;
480 for (int i = 0, c = nodes.Count; i != c; ++ i)
482 TreemapNode n = nodes[i];
483 sum += n.Size;
486 double boundsWidth = bounds.Width;
487 double boundsHeight = bounds.Height;
488 double stride = sum / Math.Min(boundsWidth, boundsHeight);
489 bool horz = bounds.Width < bounds.Height;
491 double x = bounds.Left;
492 double y = bounds.Top;
494 for (int i = 0, c = nodes.Count; i != c; ++i)
496 TreemapNode n = nodes[i];
497 double d = n.Size / stride;
499 if (horz)
501 n.Bounds = new RectD(x, y, x + d, y + stride);
502 x += d;
504 else
506 n.Bounds = new RectD(x, y, x + stride, y + d);
507 y += d;
511 if (horz)
512 remainder = new RectD(bounds.Left, bounds.Top + stride, bounds.Right, bounds.Bottom);
513 else
514 remainder = new RectD(bounds.Left + stride, bounds.Top, bounds.Right, bounds.Bottom);
517 static private void Squarify(List<TreemapNode> nodes)
519 RectD bounds = new RectD(0, 0, 1, 1);
520 int ni = 0;
522 List<TreemapNode> row = new List<TreemapNode>();
525 row.Clear();
527 double w = Math.Min(bounds.Width, bounds.Height);
529 for (int c = nodes.Count; ni != c; ++ni)
531 TreemapNode n = nodes[ni];
532 if (CompareRatio(row, n, w))
533 break;
534 row.Add(n);
537 RectD remainder;
538 LayoutRow(ref bounds, row, out remainder);
539 bounds = remainder;
541 while (ni < nodes.Count);
544 static private void LayoutTree(TreemapNode tree)
546 Squarify(tree.Children);
548 for (int i = 0, c = tree.Children.Count; i != c; ++i)
549 LayoutTree(tree.Children[i]);
552 private GLFont m_titleFont;
553 private TreemapNode m_tree;
554 private bool m_dirty;