private SolidColorBrush GetBrush(IHierarchicalData data) { if (Highlighting != null && Highlighting.IsHighlighted(data)) { return(DefaultDrawingPrimitives.HighlightBrush); } SolidColorBrush brush; if (data.ColorKey != null) { brush = _brushFactory.GetBrush(data.ColorKey); } else { // For non leaf nodes the weight is 0. We only can merge area metrics. // See HierarchyBuilder.InsertLeaf. var color = DefaultDrawingPrimitives.WhiteToRedGradient.GradientStops.GetRelativeColor(data.NormalizedWeightMetric); brush = new SolidColorBrush(color); brush.Freeze(); } return(brush); }
private CircularLayoutInfo CalculateEnclosingCircle(IHierarchicalData root, FrontChain frontChain) { // Get extreme points by extending the vector from origin to center by the radius var layouts = frontChain.ToList(); var points = layouts.Select(x => Geometry.MovePointAlongLine(new Point(), x.Center, x.Radius)).ToList(); var layout = GetLayout(root); Debug.Assert(layout.Center == new Point()); // Since we process bottom up this node was not considered yet. double radius; Point center; if (frontChain.Head.Next == frontChain.Head) // Single element { center = frontChain.Head.Value.Center; radius = frontChain.Head.Value.Radius; } else { // 2 and 3 points seem to be handled correctly. Geometry.FindMinimalBoundingCircle(points, out center, out radius); } layout.Center = center; Debug.Assert(Math.Abs(radius) > 0.00001); layout.Radius = radius * 1.03; // Just a little larger 3%. return(layout); // Note that this is not exact. It is a difficult problem. // We may have a little overlap. The larger the increment in radius // the smaller this problem gets. }
private CircularLayoutInfo GetLayout(IHierarchicalData item) { var layout = item.Layout as CircularLayoutInfo; Debug.Assert(layout != null); return(layout); }
private void Squarify(Rect availableSpace, IHierarchicalData data) { if (DebugEnabled) { // Print the item and the available space. _level++; Debug.WriteLine(GetLevelIndent() + "Squarify - " + data.Name + " Available=" + Format(availableSpace)); } if (data.Children.Count == 0) { // Draw leaf node at its location //DrawRectangle(availableSpace, data); No, just calculate the layout! if (DebugEnabled) { var layout = GetLayout(data); Debug.WriteLine(GetLevelIndent() + "Printing " + data.Name + ", " + "Rect=" + Format(layout.Rect)); } _level--; return; } var itemsToArrange = new List <IHierarchicalData>(data.Children); Squarify(availableSpace, itemsToArrange); _level--; }
public bool IsHighlighted(IHierarchicalData data) { var isNameMatchingActive = !string.IsNullOrEmpty(_pattern); var isNameMatching = false; if (isNameMatchingActive) { isNameMatching = data.Name.ToLowerInvariant().Contains(_pattern); } // Matching area and weight criteria is optional. var areRangesMatching = _toolViewModel.NoFilterJustHighlight && _toolViewModel.IsAreaValid(data.AreaMetric) && _toolViewModel.IsWeightValid(data.WeightMetric); if (!isNameMatchingActive && _toolViewModel.NoFilterJustHighlight) { // If no name is entered ignore the name filtering return(areRangesMatching); } if (isNameMatchingActive && !_toolViewModel.NoFilterJustHighlight) { return(isNameMatching); } if (isNameMatchingActive && _toolViewModel.NoFilterJustHighlight) { return(isNameMatching && areRangesMatching); } return(false); }
/// <summary> /// Layout must have been called /// </summary> public IHierarchicalData Hit(IHierarchicalData item, Point mousePos) { // We may find a more detailed hit deeper. IHierarchicalData best = null; if (item.Layout == null) { return(null); } if (item.Layout.IsHit(mousePos)) { best = item; if (item.IsLeafNode) { return(item); } } foreach (var child in item.Children) { if (child.Layout.IsHit(mousePos)) { return(Hit(child, mousePos)); } } return(best); }
public void Layout(IHierarchicalData root, double width, double height) { var rect = new Rect(new Point(0, 0), new Size(width, height)); var layout = GetLayout(root); layout.Rect = rect; Squarify(rect, root); }
private static RectangularLayoutInfo GetLayout(IHierarchicalData data) { if (data.Layout == null) { data.Layout = new RectangularLayoutInfo(); } return(data.Layout as RectangularLayoutInfo); }
public void LoadData(IHierarchicalData data) { _data = data; // Layout once. Later we scale it appropriately var layout = new CirclePackingLayout(); layout.Layout(_data, double.MaxValue, double.MaxValue); }
protected override void InitPopup(IHierarchicalData hit) { _popupText.Text = hit.Description; _popup.PlacementTarget = GetCanvas(); _popup.Placement = PlacementMode.Mouse; _popup.Visibility = Visibility.Visible; _popup.IsOpen = true; }
/// <summary> /// At the beginning any leaf node is simply centered at (0,0) /// </summary> private void InitLayoutForLeafNode(IHierarchicalData data) { // Not interested in non leaf nodes, so we can ask for area metric. Debug.Assert(data.AreaMetric > 0); var layout = GetLayout(data); layout.Radius = AreaToRadius(data.AreaMetric); layout.Center = new Point(0, 0); layout.PendingChildrenMovement = new Vector(0, 0); }
private CircularLayoutInfo GetLayout(IHierarchicalData item) { if (item.Layout == null) { var layout = new CircularLayoutInfo(); item.Layout = layout; } return(item.Layout as CircularLayoutInfo); }
public IHierarchicalData GetMandatorySubnode(string sXPath) { IHierarchicalData Result = GetSubnode(sXPath); if (Result == null) { throw new TisException("node for path [{0}] not exist", sXPath); } return(Result); }
/// <summary> /// For performance optimization we did not apply movements to children until /// all positions are fixed. /// </summary> private void ApplyPendingMovements(IHierarchicalData data) { var layout = GetLayout(data); var pending = layout.PendingChildrenMovement; foreach (var child in data.Children) { var childLayout = GetLayout(child); childLayout.Move(pending); ApplyPendingMovements(child); } }
private void Draw(DrawingContext dc, IHierarchicalData data) { var brush = GetBrush(data); var layout = GetLayout(data); dc.DrawEllipse(brush, _pen, layout.Center, layout.Radius, layout.Radius); foreach (var child in data.Children) { Draw(dc, child); } }
public IHierarchicalData[] GetSubnodes(string sXPath) { IHierarchicalData[] Subnodes = null; XmlNodeList Nodes = m_BaseNode.SelectNodes(sXPath); Subnodes = new IHierarchicalData[Nodes.Count]; for (int i = 0; i < Subnodes.Length; i++) { Subnodes[i] = new XmlHierarchicalData(Nodes[i]); } return(Subnodes); }
public void Layout(IHierarchicalData root, double actualWidth, double actualHeight) { // actualWidth and actualHeight are not used. We render the circles independent of the sizes and scale them later. // Counters over all steps DebugHelper.ResetDbgCounter(); // Cleanup previous layouting root.TraverseTopDown(x => x.Layout = null); Layout(root); // Center root node to screen. Move to (0,0) var rootLayout = GetLayout(root); MoveCircles(root, -(Vector)rootLayout.Center); ApplyPendingMovements(root); }
private void RenderToDrawingContext(DrawingContext dc, IHierarchicalData data) { _level++; if (data.IsLeafNode) { var brush = GetBrush(data); //dc.DrawRectangle(_gradient, _pen, data.Layout.Rect); var layout = GetLayout(data); dc.DrawRectangle(brush, DefaultDrawingPrimitives.BlackPen, layout.Rect); } foreach (var child in data.Children) { RenderToDrawingContext(dc, child); } _level--; }
public bool Fill(ContextMenu menu, IHierarchicalData data) { if (!_menuItemToAction.Any()) { return(false); } foreach (var pair in _menuItemToAction) { var menuItem = pair.Key; // Detach context menu items from previous shown context menu (if any) var parent = menuItem.Parent as ContextMenu; parent?.Items.Clear(); menuItem.IsEnabled = data != null && data.IsLeafNode; menuItem.Command = new DelegateCommand(() => OnMenuClick(menuItem, data)); menu.Items.Add(menuItem); } return(true); }
private static object[] CreateCtorParams( IHierarchicalData InitParams, object[] additionalCtorParams) { int actualParamsSize = 1; if (additionalCtorParams != null) { actualParamsSize += additionalCtorParams.Length; } object[] actualParams = new object[actualParamsSize]; if (additionalCtorParams != null) { additionalCtorParams.CopyTo(actualParams, 0); } actualParams[actualParamsSize - 1] = InitParams; return(actualParams); }
public static object[] CreateInstances( IHierarchicalData[] instanceParamsList, object[] additionalCtorParams) { int objectsCount = instanceParamsList.Length; object[] objects = new object[objectsCount]; for (int i = 0; i < objectsCount; i++) { IHierarchicalData instanceParams = instanceParamsList[i]; // Get the instance implementation class string implClassName = instanceParams.GetMandatoryString("ImplClassName"); // Create ctor params object[] actualCtorParams = CreateCtorParams(instanceParams, additionalCtorParams); // Create instance and add to the map objects[i] = CreateInstance(implClassName, actualCtorParams); } return(objects); }
public HierarchicalDataContext(IHierarchicalData data) { Data = data; BrushFactory = null; }
public HierarchicalDataContext(IHierarchicalData data, IBrushFactory brushFactory) { Data = data; BrushFactory = brushFactory; }
public HierarchicalDataContext(IHierarchicalData data) { Data = data; BrushFactory = new ColorScheme(); }
/// <summary> /// Gets description for a branch / folder /// </summary> private string GetDescription(IHierarchicalData branch) { return(branch.GetPathToRoot()); }
private void OnMenuClick(MenuItem item, IHierarchicalData data) { // Invoke subscriber with clicked data item. _menuItemToAction[item].Invoke(data); }
private static RectangularLayoutInfo GetLayout(IHierarchicalData data) { return(data.Layout as RectangularLayoutInfo); }
/// <summary> /// Ensure that SumAreaMetrics and NormalizeWeightMetric was called and /// no node has an area of 0. /// </summary> public void LoadData(IHierarchicalData data) { _data = data; }
/// <summary> /// m, n, j and i refer to the paper. /// </summary> private void Layout(IHierarchicalData data) { if (DebugEnabled) { DebugHelper.DeleteDebugOutput(); } if (data.IsLeafNode) { // That's the most simple layout. A leaf node is centered around (0,0) InitLayoutForLeafNode(data); return; } foreach (var child in data.Children) { // First layout all levels below. Layout(child); } // The radii of all children are known here. // We start again at origin (0,0) and move the circles around using the front chain. // To initialize the front chain we use the first three circles. // Note that we have to sort by the radius and not(!) by the AreaMetric because // the area metric is used only for the leaf nodes. This was a subtle bug. // Non leaf nodes have a radius depending on the circle enclosing all children. var children = data.Children.OrderByDescending(x => GetLayout(x).Radius).ToList(); var frontChain = InitializeFrontChain(children); var adjustingFrontChain = false; Node mNode = null; Node nNode = null; // Arrange the remaining circles var childIdx = 3; while (childIdx < children.Count) { DebugHelper.IncDbgCounter(); var iItem = children.ElementAt(childIdx); var iLayout = GetLayout(iItem); if (!adjustingFrontChain) { // Find circle with shortest distance to origin mNode = frontChain.FindMinValue(DistanceToOrigin); nNode = mNode.Next; } else { // Use the specified indices from the last front chain update. } // Calc solutions // Test front chain Both violate-> delete and try to fix whole. var solutions = FindTangentCircle(mNode.Value, nNode.Value, iLayout.Radius); var tmpCircle = SelectCircle(mNode.Value, nNode.Value, solutions.Item1, solutions.Item2); Debug.Assert(tmpCircle != null); // Find overlapping with circles in the front chain.Does one intersect with the just calculated circle? // Exclude mIndex and nIndex Overlapping with itself is detected due to rounding errors. var jNode = FindOverlappingCircleOnFrontChain(frontChain, tmpCircle); if (jNode != null && adjustingFrontChain) { // This is a valid case: // See Examples\Why_we_end_up_with_a_collision_again.html } adjustingFrontChain = false; if (jNode != null && frontChain.IsAfter(jNode, mNode)) { // This is also the default case if distance j...m and n...j is the same. // IsAfter is true in both cases then. // This way always the m node is removed. m is the node closest to the origin. This seems to be the // best heuristic if we want to grow outwards. Otherwise it happened that the front // chain is reduced to less than three circles. adjustingFrontChain = true; // j is before m // Remove (j, n) from front chain // i should be placed between j and n in the next loop. // Update front chain frontChain.Delete(jNode, nNode); Debug.Assert(frontChain.Count() >= 3); // Adjust the indices mNode = jNode; // nNode stays the same } else if (jNode != null && frontChain.IsAfter(nNode, jNode)) { adjustingFrontChain = true; // j is after n // Remove (m,j) from the front chain // i should be placed between m and j in the next loop. //DebugHelper.BreakOn(13025); // Update front chain frontChain.Delete(mNode, jNode); Debug.Assert(frontChain.Count() >= 3); // mNode stays the same nNode = jNode; } else { Debug.Assert(jNode == null); // No overlap with any other circle var disp = tmpCircle.Center - iLayout.Center; MoveCircles(iItem, disp); frontChain.InsertAfter(mNode, tmpCircle); childIdx++; if (DebugEnabled) { // Template for debug output DebugHelper.WriteDebugOutput(frontChain, $"dbgOut_m_{frontChain.IndexOf(mNode)}_n_${frontChain.IndexOf(nNode)}", solutions.Item1, solutions.Item2); DebugHelper.WriteDebugOutput(frontChain, $"dbgOut_m_{frontChain.IndexOf(mNode)}_n_{frontChain.IndexOf(nNode)}", tmpCircle); } } } var enclosing = CalculateEnclosingCircle(data, frontChain); }
/// <summary> /// Moves the circle and records that this movement is pending for the children. /// </summary> private void MoveCircles(IHierarchicalData data, Vector offset) { var layout = GetLayout(data); layout.Move(offset); }