/// <summary>Recomputes the diagram layout based on the current settings.</summary> public void Layout() { if (IsEmpty || IsAnimating) return; // define the visible nodes var visibleNodes = new HashSet<BplClass>(); Func<BplClass, bool> isVisible = c => visibleNodes.Contains(c); foreach (var node in Nodes) { var bplClass = node.Class; if (ClassFilter(bplClass)) { node.ComputedVisibility = Visibility.Visible; visibleNodes.Add(bplClass); } else { node.ComputedVisibility = Visibility.Hidden; } } // define the visible blocks var isClustered = _check(DisplayOptions.GroupByNamespace) && (_check(LayoutMethod.Hierarchic) || _check(LayoutMethod.Spring)); foreach (var block in Blocks) { if (isClustered && block.Namespace.Classes.Any(c => isVisible(c))) { block.ComputedVisibility = Visibility.Visible; } else { block.ComputedVisibility = Visibility.Hidden; } } // define the visible edges Edges.Clear(); EdgesLayer.Children.Clear(); SkeletonLayer.Children.Clear(); var visibleEdges = new List<DiagramEdgeInfo>(); Action<Func<BplProperty, bool>, DiagramEdgeRole, int> addEdges = (selector, role, weight) => { foreach (var source in visibleNodes) { var parentIsVisible = source.BaseClass != null && isVisible(source.BaseClass); var properties = (parentIsVisible ? source.OwnProperties : source.Properties); if (source.Operation != null && source.Operation.Output != null) { if (parentIsVisible) { properties = properties.Union(source.Operation.Output.OwnProperties); } else { properties = properties.Union(source.Operation.Output.Properties); } } foreach (var property in properties.Where(selector)) { var target = property.ReferencedClass; if (isVisible(target)) { visibleEdges.Add(new DiagramEdgeInfo { Label = property.Name, Role = role | (property.IsCollection || property.IsArray ? DiagramEdgeRole.ZeroToMany : DiagramEdgeRole.ZeroToOne), Source = Nodes[source], Target = Nodes[target], Weight = weight }); } } } }; if (visibleNodes.Count > 0) { // define the inheritance edges if (_check(DisplayOptions.ShowInheritance)) { foreach (var target in visibleNodes) { var source = target.BaseClass; while (source != null && !isVisible(source)) source = source.BaseClass; if (source != null) { visibleEdges.Add(new DiagramEdgeInfo { Role = DiagramEdgeRole.Inheritance, Source = Nodes[source], Target = Nodes[target], Weight = 10 }); } } } // define the containement edges if (_check(DisplayOptions.ShowContainment)) { addEdges(prop => prop.IsContainer, DiagramEdgeRole.Containment, 5); } // define the strong association edges if (_check(DisplayOptions.ShowAssociations)) { addEdges(prop => prop.IsAssociation, DiagramEdgeRole.Association, 1); } // define the weak association edges if (_check(DisplayOptions.ShowAssociations)) { addEdges(prop => prop.IsWeakAssociation, DiagramEdgeRole.WeakAssociation, 1); } // define the collaboration edges if (_check(DisplayOptions.ShowCollaborations)) { foreach (var bplClass in visibleNodes.Where(c => !c.Collaborations.IsEmpty)) { foreach (var tuple in bplClass.Collaborations.Where(tuple => isVisible(tuple.Value))) { visibleEdges.Add(new DiagramEdgeInfo { Label = tuple.Key, Role = DiagramEdgeRole.WeakAssociation | DiagramEdgeRole.ZeroToMany, Source = Nodes[bplClass], Target = Nodes[tuple.Value], Weight = 1 }); } } } // define the association edges between service operations and failures if (_check(DisplayOptions.ShowCollaborations)) { foreach (var bplClass in visibleNodes.Where(c => c.Operation != null && c.Operation.Failures != null)) { foreach (var failure in bplClass.Operation.Failures.Where(f => isVisible(f))) { visibleEdges.Add(new DiagramEdgeInfo { Label = "Failure", Role = DiagramEdgeRole.WeakAssociation | DiagramEdgeRole.ZeroToMany, Source = Nodes[bplClass], Target = Nodes[failure], Weight = 1 }); } } } } // hide disconnected nodes if (_check(DisplayOptions.HideDisconnected)) { var connected = new HashSet<DiagramNode>(); foreach (var edge in visibleEdges) { connected.Add(edge.Source); connected.Add(edge.Target); } if (connected.Count > 0) { foreach (var node in Nodes.Except(connected)) { node.ComputedVisibility = Visibility.Hidden; } } } // layout the diagram var oldSize = ComputedSize; DiagramLayout.ComputeLayout(this, visibleEdges, isClustered); if (_isFirstLayout || _suspendAnimations || AnimationSpeed.TotalMilliseconds < 20) { ContentBounds.Width = ComputedSize.Width; ContentBounds.Height = ComputedSize.Height; Width = ComputedSize.Width; Height = ComputedSize.Height; Blocks.Apply(b => b.LayoutChanged()); Nodes.Apply(n => n.LayoutChanged()); Edges.Apply(e => e.Draw(this)); _isFirstLayout = false; _suspendAnimations = false; HideAdorners(); } else { var skeleton = new DiagramSkeleton(Edges); SkeletonLayer.Children.Add(skeleton); var prototype = new EffectData { Duration = AnimationSpeed, Interpolator = Interpolators.CubicOut }; var timeline = new ParallelEffect(); timeline.Add(new ResizeEffect(prototype) { Target = this, From = oldSize, To = ComputedSize }); timeline.Add(new ResizeEffect(prototype) { Target = ContentBounds, From = oldSize, To = ComputedSize }); timeline.Add(new NumericEffect(prototype) { Target = skeleton, Property=DiagramSkeleton.StrobeProperty, From = 0.0, To = 1.0}); Nodes.Apply(n => n.ApplyLayoutEffect(timeline, prototype)); Blocks.Apply(b => b.LayoutChanged()); HideAdorners(); IsAnimating = true; timeline.Play(c => { SkeletonLayer.Children.Clear(); Edges.Apply(e => e.Draw(this)); IsAnimating = false; HideAdorners(); }); } }
/// <summary/> protected internal override Effect Decompose() { var effect = new ParallelEffect(); if (FromCenter.CertainlyDifferent(ToCenter)) { effect.Add(new CenterShiftEffect(this) { From = FromCenter, To = ToCenter }); } if (FromRotate.CertainlyDifferent(ToRotate)) { effect.Add(new RotateEffect(this) { From = FromRotate, To = ToRotate }); } if (FromScale.CertainlyDifferent(ToScale) || FromScaleX.CertainlyDifferent(ToScaleX) || FromScaleY.CertainlyDifferent(ToScaleY)) { effect.Add(new ScaleEffect(this) { From = FromScale, To = ToScale, FromScaleX = FromScaleX, ToScaleX = ToScaleX, FromScaleY = FromScaleY, ToScaleY = ToScaleY }); } if (FromSkewX.CertainlyDifferent(ToSkewX) || FromSkewY.CertainlyDifferent(ToSkewY)) { effect.Add(new SkewEffect(this) { FromSkewX = FromSkewX, ToSkewX = ToSkewX, FromSkewY = FromSkewY, ToSkewY = ToSkewY }); } if (FromPosition.CertainlyDifferent(ToPosition) || FromX.CertainlyDifferent(ToX) || FromY.CertainlyDifferent(ToY)) { effect.Add(new MoveEffect(this) { From = FromPosition, To = ToPosition, FromX = FromX, ToX = ToX, FromY = FromY, ToY = ToY }); } if (FromSize.CertainlyDifferent(ToSize) || FromWidth.CertainlyDifferent(ToWidth) || FromHeight.CertainlyDifferent(ToHeight)) { effect.Add(new ResizeEffect(this) { From = FromSize, To = ToSize, FromWidth = FromWidth, ToWidth = ToWidth, FromHeight = FromHeight, ToHeight = ToHeight }); } return effect; }
/// <summary/> internal void ApplyLayoutEffect(ParallelEffect timeline, Effect prototype) { if (ComputedVisibility != Visibility) { Visibility = ComputedVisibility; if (Visibility == Visibility.Visible) { X = Canvas.ActualWidth / 2; Y = Canvas.ActualHeight / 2; } } if (Visibility == Visibility.Visible) { timeline.Add(new MoveEffect(prototype) { Target = this, ToX = ComputedPosition.X, ToY = ComputedPosition.Y }); } }