From 5b4a8b17146c7fdc5cea88ec33b73448dc0f08ac Mon Sep 17 00:00:00 2001 From: zHaytam Date: Wed, 3 Feb 2021 21:10:53 +0100 Subject: [PATCH 01/67] Seperate Nodes/Links layers --- .../ReconnectLinksToClosestPorts.razor.cs | 8 +- .../Demos/CustomGroup/Demo.razor.cs | 6 +- .../SharedDemo/Demos/CustomLink/Demo.razor.cs | 9 +- samples/SharedDemo/Demos/CustomNode.razor.cs | 5 +- .../SharedDemo/Demos/CustomPort/Demo.razor.cs | 7 +- samples/SharedDemo/Demos/DragAndDrop.razor.cs | 2 +- .../Demos/DynamicInsertions.razor.cs | 12 +- samples/SharedDemo/Demos/Events.razor.cs | 29 ++- samples/SharedDemo/Demos/Grouping.razor.cs | 6 +- samples/SharedDemo/Demos/Locked.razor.cs | 11 +- samples/SharedDemo/Demos/Performance.razor.cs | 14 +- samples/SharedDemo/Demos/Simple.razor.cs | 6 +- samples/SharedDemo/Demos/SnapToGrid.razor.cs | 6 +- samples/SharedDemo/Demos/ZoomToFit.razor.cs | 4 +- samples/SharedDemo/QuickStart.razor | 4 +- .../LinksReconnectionAlgorithms.cs | 2 +- .../Default/DeleteSelectionSubManager.cs | 14 +- .../Default/DragNewLinkSubManager.cs | 13 +- src/Blazor.Diagrams.Core/DiagramManager.cs | 181 +++++------------- src/Blazor.Diagrams.Core/Layer.cs | 47 +++++ .../Components/DiagramCanvas.razor | 5 +- .../Components/Groups/GroupNodes.razor | 2 + .../Components/Navigator.razor | 16 +- .../Components/Navigator.razor.cs | 24 ++- .../Components/Renderers/NodeRenderer.cs | 1 + 25 files changed, 186 insertions(+), 248 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Layer.cs diff --git a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs index 3ec84b7e..bfaac7fa 100644 --- a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs +++ b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs @@ -17,11 +17,9 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(300, 50); - diagramManager.AddLink(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Right)); - diagramManager.AddLink(node1.GetPort(PortAlignment.Bottom), node3.GetPort(PortAlignment.Top)); - diagramManager.AddNode(node1); - diagramManager.AddNode(node2); - diagramManager.AddNode(node3); + diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Right))); + diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Bottom), node3.GetPort(PortAlignment.Top))); + diagramManager.Nodes.Add(node1, node2, node3); } diff --git a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs index ae8755ec..f152198d 100644 --- a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs @@ -22,11 +22,11 @@ protected override void OnInitialized() var node2 = NewNode(300, 300); var node3 = NewNode(500, 100); - _diagramManager.AddNode(node1); + _diagramManager.Nodes.Add(node1, node2, node3); _diagramManager.AddGroup(new CustomGroupModel(_diagramManager, new[] { node2, node3 }, "Group 1")); - _diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); - _diagramManager.AddLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Bottom)); + _diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + _diagramManager.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs index b95b9d02..565b8e20 100644 --- a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs @@ -23,12 +23,9 @@ protected override void OnInitialized() var node2 = NewNode(300, 300); var node3 = NewNode(500, 50); - _diagramManager.AddNode(node1); - _diagramManager.AddNode(node2); - _diagramManager.AddNode(node3); - - _diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); - _diagramManager.AddLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)); + _diagramManager.Nodes.Add(node1, node2, node3); + _diagramManager.Links.Add(new ThickLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + _diagramManager.Links.Add(new ThickLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomNode.razor.cs b/samples/SharedDemo/Demos/CustomNode.razor.cs index f644c08c..4fa9abdb 100644 --- a/samples/SharedDemo/Demos/CustomNode.razor.cs +++ b/samples/SharedDemo/Demos/CustomNode.razor.cs @@ -14,15 +14,14 @@ protected override void OnInitialized() base.OnInitialized(); diagramManager.RegisterModelComponent(); - diagramManager.AddNode(NewNode(100, 100)); - diagramManager.AddNode(NewNode(300, 300)); var node = new NodeModel(new Point(20, 20)); node.AddPort(PortAlignment.Top); node.AddPort(PortAlignment.Right); node.AddPort(PortAlignment.Bottom); node.AddPort(PortAlignment.Left); - diagramManager.AddNode(node); + + diagramManager.Nodes.Add(node, NewNode(100, 100), NewNode(300, 300)); } private BotAnswerNode NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs index 2d799e83..b866b4d4 100644 --- a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs @@ -21,11 +21,8 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - _diagramManager.AddNode(node1); - _diagramManager.AddNode(node2); - _diagramManager.AddNode(NewNode(500, 50)); - - _diagramManager.AddLink(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top)); + _diagramManager.Nodes.Add(node1, node2, NewNode(500, 50)); + _diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/DragAndDrop.razor.cs b/samples/SharedDemo/Demos/DragAndDrop.razor.cs index da63abda..a23d1466 100644 --- a/samples/SharedDemo/Demos/DragAndDrop.razor.cs +++ b/samples/SharedDemo/Demos/DragAndDrop.razor.cs @@ -35,7 +35,7 @@ private void OnDrop(DragEventArgs e) var node = _draggedType == 0 ? new NodeModel(position) : new BotAnswerNode(position); node.AddPort(PortAlignment.Top); node.AddPort(PortAlignment.Bottom); - _diagramManager.AddNode(node); + _diagramManager.Nodes.Add(node); _draggedType = null; } } diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs index 1f4fc0ea..6a8099b7 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs @@ -21,20 +21,20 @@ protected override void OnInitialized() var node1 = new NodeModel(new Point(300, 50)); var node2 = new NodeModel(new Point(300, 400)); - diagramManager.AddNodes(node1, node2); + diagramManager.Nodes.Add(node1, node2); } protected void AddNode() { var x = _random.Next(0, (int)diagramManager.Container.Width - 120); var y = _random.Next(0, (int)diagramManager.Container.Height - 100); - diagramManager.AddNode(new NodeModel(new Point(x, y))); + diagramManager.Nodes.Add(new NodeModel(new Point(x, y))); } protected void RemoveNode() { var i = _random.Next(0, diagramManager.Nodes.Count); - diagramManager.RemoveNode(diagramManager.Nodes.ElementAt(i)); + diagramManager.Nodes.Remove(diagramManager.Nodes[i]); } protected void AddPort() @@ -68,9 +68,7 @@ protected void RemovePort() var i = _random.Next(0, node.Ports.Count); var port = node.Ports[i]; - foreach (var link in port.Links) - diagramManager.RemoveLink(link); - + diagramManager.Links.Remove(port.Links.ToArray()); node.RemovePort(port); node.Refresh(); } @@ -88,7 +86,7 @@ protected void AddLink() var sourcePort = node1.Ports[_random.Next(0, node1.Ports.Count)]; var targetPort = node2.Ports[_random.Next(0, node2.Ports.Count)]; - diagramManager.AddLink(sourcePort, targetPort); + diagramManager.Links.Add(new LinkModel(sourcePort, targetPort)); } } } diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index 9cd922ed..7ec07ff7 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -3,6 +3,7 @@ using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using System.Collections.Generic; +using System.Linq; namespace SharedDemo.Demos { @@ -19,11 +20,8 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.AddNode(node1); - diagramManager.AddNode(node2); - diagramManager.AddNode(NewNode(300, 50)); - - diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); + diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); } private void RegisterEvents() @@ -34,8 +32,8 @@ private void RegisterEvents() StateHasChanged(); }; - diagramManager.NodeAdded += (n) => events.Add($"NodeAdded, NodeId={n.Id}"); - diagramManager.NodeRemoved += (n) => events.Add($"NodeRemoved, NodeId={n.Id}"); + diagramManager.Nodes.Added += (nodes) => events.AddRange(nodes.Select(n => $"NodesAdded, NodeId={n.Id}")); + diagramManager.Nodes.Removed += (nodes) => events.AddRange(nodes.Select(n => $"NodesRemoved, NodeId={n.Id}")); diagramManager.SelectionChanged += (m, s) => { @@ -43,19 +41,20 @@ private void RegisterEvents() StateHasChanged(); }; - diagramManager.LinkAdded += (l) => events.Add($"LinkAdded, LinkId={l.Id}"); + diagramManager.Links.Added += (links) => events.AddRange(links.Select(l => $"Links.Added, LinkId={l.Id}")); - diagramManager.LinkAttached += (l) => - { - events.Add($"LinkAttached, LinkId={l.Id}"); - StateHasChanged(); - }; + // Todo: replace with TargetPortChanged + //diagramManager.LinkAttached += (l) => + //{ + // events.Add($"LinkAttached, LinkId={l.Id}"); + // StateHasChanged(); + //}; - diagramManager.LinkRemoved += (l) => events.Add($"LinkRemoved, LinkId={l.Id}"); + diagramManager.Links.Removed += (links) => events.AddRange(links.Select(l => $"Links.Removed, LinkId={l.Id}")); diagramManager.MouseUp += (m, e) => { - events.Add($"MouseUp, ModelId={m?.Id}"); + events.Add($"MouseUp, Type={m?.GetType().Name}, ModelId={m?.Id}"); StateHasChanged(); }; } diff --git a/samples/SharedDemo/Demos/Grouping.razor.cs b/samples/SharedDemo/Demos/Grouping.razor.cs index 770a4f21..341e8ff0 100644 --- a/samples/SharedDemo/Demos/Grouping.razor.cs +++ b/samples/SharedDemo/Demos/Grouping.razor.cs @@ -19,6 +19,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(250, 250); var node3 = NewNode(500, 100); + diagramManager.Nodes.Add(node1, node2, node3); var group = diagramManager.Group(node1, node2); group.AddPort(PortAlignment.Bottom); @@ -26,9 +27,8 @@ protected override void OnInitialized() group.AddPort(PortAlignment.Left); group.AddPort(PortAlignment.Right); - diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); - diagramManager.AddLink(group.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)); - diagramManager.AddNode(node3); + diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagramManager.Links.Add(new LinkModel(group.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/Locked.razor.cs b/samples/SharedDemo/Demos/Locked.razor.cs index 653e8c4f..de3564fb 100644 --- a/samples/SharedDemo/Demos/Locked.razor.cs +++ b/samples/SharedDemo/Demos/Locked.razor.cs @@ -15,12 +15,13 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.AddNode(node1); - diagramManager.AddNode(node2); - diagramManager.AddNode(NewNode(300, 50)); + diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); - var link = diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); - link.Locked = true; + var link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) + { + Locked = true + }; + diagramManager.Links.Add(link); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/Performance.razor.cs b/samples/SharedDemo/Demos/Performance.razor.cs index f5feef38..6fb7cc1d 100644 --- a/samples/SharedDemo/Demos/Performance.razor.cs +++ b/samples/SharedDemo/Demos/Performance.razor.cs @@ -23,20 +23,10 @@ protected override void OnInitialized() var sourcePort = node1.AddPort(PortAlignment.Right); var targetPort = node2.AddPort(PortAlignment.Left); - diagramManager.AddNodes(node1, node2); - diagramManager.AddLink(sourcePort, targetPort); + diagramManager.Nodes.Add(node1, node2); + diagramManager.Links.Add(new LinkModel(sourcePort, targetPort)); } } } - - private NodeModel NewNode(double x, double y) - { - var node = new NodeModel(new Point(x, y)); - node.AddPort(PortAlignment.Bottom); - node.AddPort(PortAlignment.Top); - node.AddPort(PortAlignment.Left); - node.AddPort(PortAlignment.Right); - return node; - } } } diff --git a/samples/SharedDemo/Demos/Simple.razor.cs b/samples/SharedDemo/Demos/Simple.razor.cs index d7ce8d6f..7934d891 100644 --- a/samples/SharedDemo/Demos/Simple.razor.cs +++ b/samples/SharedDemo/Demos/Simple.razor.cs @@ -15,10 +15,8 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); - diagramManager.AddNode(node1); - diagramManager.AddNode(node2); - diagramManager.AddNode(NewNode(300, 50)); + diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); } protected void ToggleZoom() => diagramManager.Options.Zoom.Enabled = !diagramManager.Options.Zoom.Enabled; diff --git a/samples/SharedDemo/Demos/SnapToGrid.razor.cs b/samples/SharedDemo/Demos/SnapToGrid.razor.cs index 8f7b9b03..ae246420 100644 --- a/samples/SharedDemo/Demos/SnapToGrid.razor.cs +++ b/samples/SharedDemo/Demos/SnapToGrid.razor.cs @@ -18,10 +18,8 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); - diagramManager.AddNode(node1); - diagramManager.AddNode(node2); - diagramManager.AddNode(NewNode(300, 50)); + diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/ZoomToFit.razor.cs b/samples/SharedDemo/Demos/ZoomToFit.razor.cs index 0fd0e4b2..f371f521 100644 --- a/samples/SharedDemo/Demos/ZoomToFit.razor.cs +++ b/samples/SharedDemo/Demos/ZoomToFit.razor.cs @@ -23,8 +23,8 @@ protected override void OnInitialized() var sourcePort = node1.AddPort(PortAlignment.Right); var targetPort = node2.AddPort(PortAlignment.Left); - diagramManager.AddNodes(node1, node2); - diagramManager.AddLink(sourcePort, targetPort); + diagramManager.Nodes.Add(node1, node2); + diagramManager.Links.Add(new LinkModel(sourcePort, targetPort)); } } } diff --git a/samples/SharedDemo/QuickStart.razor b/samples/SharedDemo/QuickStart.razor index 7bca9e0e..52ed8c7e 100644 --- a/samples/SharedDemo/QuickStart.razor +++ b/samples/SharedDemo/QuickStart.razor @@ -118,7 +118,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(300, 50); - DiagramManager.AddNodes(node1, node2, node3); + DiagramManager.Nodes.Add(node1, node2, node3); } private NodeModel NewNode(double x, double y) @@ -143,7 +143,7 @@ private NodeModel NewNode(double x, double y) var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(300, 50); - DiagramManager.AddNodes(node1, node2, node3); + DiagramManager.Nodes.Add(node1, node2, node3); DiagramManager.AddLink(node1.GetPort(PortAlignment.RIGHT), node2.GetPort(PortAlignment.LEFT)); }

diff --git a/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs b/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs index 368fa45c..5cad7986 100644 --- a/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs +++ b/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs @@ -13,7 +13,7 @@ public static void ReconnectLinksToClosestPorts(this DiagramManager diagramManag // Only refresh ports once var portsToRefresh = new HashSet(); - foreach (var link in diagramManager.AllLinks.ToArray()) + foreach (var link in diagramManager.Links.ToArray()) { if (link.TargetPort == null) continue; diff --git a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs b/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs index d407a35c..9b03704b 100644 --- a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs +++ b/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs @@ -16,8 +16,7 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) if (e.AltKey || e.CtrlKey || e.ShiftKey || e.Code != DiagramManager.Options.DeleteKey) return; - var somethingWasRemoved = false; - + // TODO: BATCH foreach (var sm in DiagramManager.SelectedModels.ToList()) { if (sm.Locked) @@ -25,20 +24,13 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) if (sm is NodeModel node) { - DiagramManager.RemoveNode(node, false); - somethingWasRemoved = true; + DiagramManager.Nodes.Remove(node); } else if (sm is LinkModel link) { - DiagramManager.RemoveLink(link, false); - somethingWasRemoved = true; + DiagramManager.Links.Remove(link); } } - - if (somethingWasRemoved) - { - DiagramManager.Refresh(); - } } public override void Dispose() diff --git a/src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs b/src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs index 0b70ea8b..30f629b8 100644 --- a/src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs +++ b/src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs @@ -25,7 +25,10 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) _initialX = e.ClientX; _initialY = e.ClientY; - _ongoingLink = DiagramManager.AddLink(port, null); + _ongoingLink = new LinkModel(port, null); + _ongoingLink.OnGoingPosition = new Point(port.Position.X + port.Size.Width / 2, + port.Position.Y + port.Size.Height / 2); + DiagramManager.Links.Add(_ongoingLink); } private void DiagramManager_MouseMove(Model model, MouseEventArgs e) @@ -48,12 +51,16 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) if (!(model is PortModel port) || !_ongoingLink.SourcePort.CanAttachTo(port)) { - DiagramManager.RemoveLink(_ongoingLink); + DiagramManager.Links.Remove(_ongoingLink); _ongoingLink = null; return; } - DiagramManager.AttachLink(_ongoingLink, port); + _ongoingLink.SetTargetPort(port); + _ongoingLink.Refresh(); + port.Refresh(); + _ongoingLink.SourcePort.Parent.Group?.Refresh(); + port?.Parent.Group?.Refresh(); _ongoingLink = null; } diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 1c286579..7c6fda33 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -14,7 +14,6 @@ namespace Blazor.Diagrams.Core { public class DiagramManager { - private readonly List _nodes; private readonly List _subManagers; private readonly Dictionary _componentByModelMapping; private readonly HashSet _selectedModels; @@ -27,12 +26,7 @@ public class DiagramManager public event Action? Wheel; public event Action? Changed; - public event Action? NodeAdded; - public event Action? NodeRemoved; public event Action? SelectionChanged; - public event Action? LinkAdded; - public event Action? LinkAttached; - public event Action? LinkRemoved; public event Action? GroupAdded; public event Action? GroupRemoved; @@ -42,13 +36,19 @@ public class DiagramManager public DiagramManager(DiagramOptions? options = null) { - _nodes = new List(); _subManagers = new List(); _componentByModelMapping = new Dictionary(); _selectedModels = new HashSet(); _groups = new List(); Options = options ?? new DiagramOptions(); + Nodes = new Layer(); + Links = new Layer(); + + Nodes.Added += OnNodesAdded; + Nodes.Removed += OnNodesRemoved; + Links.Added += OnLinksAdded; + Links.Removed += OnLinksRemoved; RegisterSubManager(); RegisterSubManager(); @@ -59,136 +59,64 @@ public DiagramManager(DiagramOptions? options = null) RegisterSubManager(); } - public IReadOnlyCollection Nodes => _nodes; - public IEnumerable AllLinks => _nodes.SelectMany(n => n.AllLinks).Union(_groups.SelectMany(g => g.AllLinks)).Distinct(); + public Layer Nodes { get; } + public Layer Links { get; } public IReadOnlyCollection SelectedModels => _selectedModels; - public IReadOnlyCollection Groups => _groups; + //public IReadOnlyList Nodes => _nodes; + //public IEnumerable Links => _nodes.SelectMany(n => n.AllLinks).Union(_groups.SelectMany(g => g.AllLinks)).Distinct(); + public IReadOnlyList Groups => _groups; public Rectangle? Container { get; internal set; } public Point Pan { get; internal set; } = Point.Zero; public double Zoom { get; private set; } = 1; public DiagramOptions Options { get; } - public void AddNode(NodeModel node) - { - _nodes.Add(node); - NodeAdded?.Invoke(node); - Changed?.Invoke(); - } + private void OnNodesAdded(NodeModel[] _) => Refresh(); - public void AddNodes(params NodeModel[] nodes) + private void OnNodesRemoved(NodeModel[] nodes) { - _nodes.AddRange(nodes); - - // Is this okay? foreach (var node in nodes) - NodeAdded?.Invoke(node); - - Changed?.Invoke(); - } - - public void RemoveNode(NodeModel node, bool triggerEvent = true) - { - if (_nodes.Remove(node)) - { - // In case its selected - _selectedModels.Remove(node); - - foreach (var link in node.AllLinks.ToList()) // Since we're removing from the list - { - RemoveLink(link, false); - } - - NodeRemoved?.Invoke(node); - if (triggerEvent) - { - Changed?.Invoke(); - } - } - } - - public LinkModel AddLink(PortModel source, PortModel? target = null) - { - if (Options?.Links?.DefaultLinkModel != null) { - var link = (LinkModel)Activator.CreateInstance(Options?.Links?.DefaultLinkModel, source, target); - return AddLink(link, source, target); + Links.Remove(node.AllLinks.ToArray()); } - return AddLink(source, target); - } - - ///

- /// Creates and configures an instance of a link model that expects a constructor with two parameters: - /// the source port and the target port. - /// - /// The created link instance. - public T AddLink(PortModel source, PortModel? target = null) where T : LinkModel - { - var link = (T)Activator.CreateInstance(typeof(T), source, target); - return AddLink(link, source, target); + Refresh(); } - public T AddLink(T link, PortModel source, PortModel? target = null) where T : LinkModel + private void OnLinksAdded(LinkModel[] links) { - link.Type = Options.Links.DefaultLinkType; - source.AddLink(link); - - if (target == null) + foreach (var link in links) { - link.OnGoingPosition = new Point(source.Position.X + source.Size.Width / 2, - source.Position.Y + source.Size.Height / 2); - } - else - { - target.AddLink(link); - } + link.SourcePort.AddLink(link); + link.TargetPort?.AddLink(link); - source.Refresh(); - target?.Refresh(); + link.SourcePort.Refresh(); + link.TargetPort?.Refresh(); - source.Parent.Group?.Refresh(); - target?.Parent.Group?.Refresh(); + link.SourcePort.Parent.Group?.Refresh(); + link.TargetPort?.Parent.Group?.Refresh(); + } - LinkAdded?.Invoke(link); - Changed?.Invoke(); - return link; + Refresh(); } - public void AttachLink(LinkModel link, PortModel targetPort) + private void OnLinksRemoved(LinkModel[] links) { - if (link.IsAttached) - throw new Exception("Link already attached."); - - if (!link.SourcePort.CanAttachTo(targetPort)) - return; + foreach (var link in links) + { + link.SourcePort.RemoveLink(link); + link.TargetPort?.RemoveLink(link); - link.SetTargetPort(targetPort); - link.Refresh(); - targetPort.Refresh(); + link.SourcePort.Refresh(); + link.TargetPort?.Refresh(); - link.SourcePort.Parent.Group?.Refresh(); - targetPort?.Parent.Group?.Refresh(); + link.SourcePort.Parent.Group?.Refresh(); + link.TargetPort?.Parent.Group?.Refresh(); + } - LinkAttached?.Invoke(link); + Refresh(); } - public void RemoveLink(LinkModel link, bool triggerEvent = true) - { - link.SourcePort.RemoveLink(link); - link.TargetPort?.RemoveLink(link); - - LinkRemoved?.Invoke(link); - link.SourcePort.Refresh(); - link.TargetPort?.Refresh(); - - link.SourcePort.Parent.Group?.Refresh(); - link.TargetPort?.Parent.Group?.Refresh(); - - if (triggerEvent) - { - Changed?.Invoke(); - } - } + #region Groups /// /// Groups 2 or more children. @@ -223,14 +151,8 @@ public void AddGroup(GroupModel group) foreach (var child in group.Children) { - if (child is GroupModel g) - { - _groups.Remove(g); - } - else - { - _nodes.Remove(child); - } + if (child is NodeModel node && !Nodes.Contains(node)) + throw new Exception("One of the nodes isn't in the diagram. Make sure to add all the nodes before creating the group."); } _groups.Add(group); @@ -244,23 +166,14 @@ public void Ungroup(GroupModel group) return; group.Ungroup(); - - foreach (var child in group.Children) - { - if (child is GroupModel g) - { - _groups.Add(g); - } - else - { - _nodes.Add(child); - } - } - GroupRemoved?.Invoke(group); Refresh(); } + #endregion + + #region Selection + public void SelectModel(SelectableModel model, bool unselectOthers) { if (_selectedModels.Contains(model)) @@ -302,6 +215,8 @@ public void UnselectAll() _selectedModels.Clear(); } + #endregion + public void RegisterSubManager() where T : DiagramSubManager { var type = typeof(T); @@ -343,11 +258,11 @@ public void RegisterModelComponent(Type modelType, Type componentType) public void ZoomToFit(double margin = 10) { - if (_nodes.Count == 0) + if (Nodes.Count == 0) return; var selectedNodes = SelectedModels.Where(s => s is NodeModel).Select(s => (NodeModel)s); - (var minX, var maxX, var minY, var maxY) = GetNodesRect(selectedNodes.Any() ? selectedNodes : _nodes); + (var minX, var maxX, var minY, var maxY) = GetNodesRect(selectedNodes.Any() ? selectedNodes : Nodes); var width = maxX - minX + 2 * margin; var height = maxY - minY + 2 * margin; minX -= margin; diff --git a/src/Blazor.Diagrams.Core/Layer.cs b/src/Blazor.Diagrams.Core/Layer.cs new file mode 100644 index 00000000..0049166c --- /dev/null +++ b/src/Blazor.Diagrams.Core/Layer.cs @@ -0,0 +1,47 @@ +using Blazor.Diagrams.Core.Models.Base; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Blazor.Diagrams.Core +{ + public class Layer : IReadOnlyList where T : Model + { + private readonly List _items = new List(); + + public event Action? Added; + public event Action? Removed; + + public void Add(params T[] items) + { + _items.AddRange(items); + Added?.Invoke(items); + } + + public void Remove(params T[] items) + { + foreach (var item in items) + { + _items.Remove(item); + } + + // May contain items not removed (because they weren't in the list) + // It's up to the user to only give existing items + Removed?.Invoke(items); + } + + public bool Contains(T item) => _items.Contains(item); + + public void Clear() + { + var items = _items.ToArray(); + _items.Clear(); + Removed?.Invoke(items); + } + + public int Count => _items.Count; + public T this[int index] => _items[index]; + public IEnumerator GetEnumerator() => _items.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index e1a6b440..0b659860 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -14,8 +14,9 @@ @* Links *@ - @foreach (var link in DiagramManager.AllLinks.Where(l => l.SourcePort.Parent.Group == null && l.TargetPort?.Parent.Group == null)) + @foreach (var link in DiagramManager.Links) { + @link.Id } @@ -34,7 +35,7 @@ } - @foreach (var node in DiagramManager.Nodes.Where(n => n.Layer == RenderLayer.HTML)) + @foreach (var node in DiagramManager.Nodes.Where(n => n.Layer == RenderLayer.HTML && n.Group == null)) { } diff --git a/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor b/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor index e1a5f0ec..a9422cb0 100644 --- a/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor +++ b/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor @@ -9,10 +9,12 @@ { if (node is GroupModel g) { + Console.WriteLine("deep"); } else { + Console.WriteLine(node.Id); } } diff --git a/src/Blazor.Diagrams/Components/Navigator.razor b/src/Blazor.Diagrams/Components/Navigator.razor index af6465f7..1ae08e71 100644 --- a/src/Blazor.Diagrams/Components/Navigator.razor +++ b/src/Blazor.Diagrams/Components/Navigator.razor @@ -42,24 +42,10 @@ - - @foreach (var child in group.Children) - { - var childLeft = (Math.Max(0, child.Position.X * DiagramManager.Zoom) + addedNodeX) * XFactor; - var childTop = (Math.Max(0, child.Position.Y * DiagramManager.Zoom) + addedNodeY) * YFactor; - var childWidth = child.Size.Width * DiagramManager.Zoom * XFactor; - var childHeight = child.Size.Height * DiagramManager.Zoom * YFactor; - - - - - } } diff --git a/src/Blazor.Diagrams/Components/Navigator.razor.cs b/src/Blazor.Diagrams/Components/Navigator.razor.cs index d87010d3..4c0ec29b 100644 --- a/src/Blazor.Diagrams/Components/Navigator.razor.cs +++ b/src/Blazor.Diagrams/Components/Navigator.razor.cs @@ -36,17 +36,29 @@ protected override void OnParametersSet() group.Changed += Refresh; DiagramManager.Changed += DiagramManager_Changed; - DiagramManager.NodeAdded += DiagramManager_NodeAdded; - DiagramManager.NodeRemoved += DiagramManager_NodeRemoved; + DiagramManager.Nodes.Added += DiagramManager_NodesAdded; + DiagramManager.Nodes.Removed += DiagramManager_NodesRemoved; DiagramManager.GroupAdded += DiagramManager_GroupAdded; DiagramManager.GroupRemoved += DiagramManager_GroupRemoved; } private void DiagramManager_Changed() => Refresh(); - private void DiagramManager_NodeAdded(NodeModel node) => node.Changed += Refresh; + private void DiagramManager_NodesAdded(NodeModel[] nodes) + { + foreach (var node in nodes) + { + node.Changed += Refresh; + } + } - private void DiagramManager_NodeRemoved(NodeModel node) => node.Changed -= Refresh; + private void DiagramManager_NodesRemoved(NodeModel[] nodes) + { + foreach (var node in nodes) + { + node.Changed -= Refresh; + } + } private void DiagramManager_GroupAdded(GroupModel group) => group.Changed += Refresh; @@ -184,8 +196,8 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref public void Dispose() { DiagramManager.Changed -= DiagramManager_Changed; - DiagramManager.NodeAdded -= DiagramManager_NodeAdded; - DiagramManager.NodeRemoved -= DiagramManager_NodeRemoved; + DiagramManager.Nodes.Added -= DiagramManager_NodesAdded; + DiagramManager.Nodes.Removed -= DiagramManager_NodesRemoved; // Todo: unregister node/group changed events } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index 5f5c3869..38c6f938 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -93,6 +93,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.OpenElement(0, Node.Layer == RenderLayer.HTML ? "div" : "g"); builder.AddAttribute(1, "class", $"node{(Node.Locked ? " locked" : string.Empty)}"); + builder.AddAttribute(55, "data-id", Node.Id); if (Node.Layer == RenderLayer.HTML) { From b2033f9a488177585c9cb46e3d979c2f91b5bd66 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Wed, 3 Feb 2021 22:41:02 +0100 Subject: [PATCH 02/67] fix: Remove links when group is removed --- src/Blazor.Diagrams.Core/DiagramManager.cs | 1 + src/Blazor.Diagrams.Core/Layer.cs | 9 +++++++++ src/Blazor.Diagrams.Core/Models/GroupModel.cs | 1 - src/Blazor.Diagrams/Components/Groups/GroupNodes.razor | 2 -- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 7c6fda33..4c4f1771 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -166,6 +166,7 @@ public void Ungroup(GroupModel group) return; group.Ungroup(); + Links.Remove(group.AllLinks.ToArray()); GroupRemoved?.Invoke(group); Refresh(); } diff --git a/src/Blazor.Diagrams.Core/Layer.cs b/src/Blazor.Diagrams.Core/Layer.cs index 0049166c..d5d1e638 100644 --- a/src/Blazor.Diagrams.Core/Layer.cs +++ b/src/Blazor.Diagrams.Core/Layer.cs @@ -14,12 +14,18 @@ public class Layer : IReadOnlyList where T : Model public void Add(params T[] items) { + if (items.Length == 0) + return; + _items.AddRange(items); Added?.Invoke(items); } public void Remove(params T[] items) { + if (items.Length == 0) + return; + foreach (var item in items) { _items.Remove(item); @@ -34,6 +40,9 @@ public void Remove(params T[] items) public void Clear() { + if (Count == 0) + return; + var items = _items.ToArray(); _items.Clear(); Removed?.Invoke(items); diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs index a7e70985..20922e0a 100644 --- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs +++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs @@ -1,5 +1,4 @@ using Blazor.Diagrams.Core.Models.Core; -using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor b/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor index a9422cb0..e1a5f0ec 100644 --- a/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor +++ b/src/Blazor.Diagrams/Components/Groups/GroupNodes.razor @@ -9,12 +9,10 @@ { if (node is GroupModel g) { - Console.WriteLine("deep"); } else { - Console.WriteLine(node.Id); } } From 87f69de3748610f357a3ca4565242938204de976 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Wed, 3 Feb 2021 23:08:55 +0100 Subject: [PATCH 03/67] feat: Add RemoveGroup method which deletes all members + GroupUngrouped event GroupRemoved event is now used in the new method instead --- .../Default/DeleteSelectionSubManager.cs | 6 +++++- src/Blazor.Diagrams.Core/DiagramManager.cs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs b/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs index 9b03704b..adbff1db 100644 --- a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs +++ b/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs @@ -22,7 +22,11 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) if (sm.Locked) continue; - if (sm is NodeModel node) + if (sm is GroupModel group) + { + DiagramManager.RemoveGroup(group); + } + else if (sm is NodeModel node) { DiagramManager.Nodes.Remove(node); } diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 4c4f1771..0e7a07eb 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -28,6 +28,7 @@ public class DiagramManager public event Action? Changed; public event Action? SelectionChanged; public event Action? GroupAdded; + public event Action? GroupUngrouped; public event Action? GroupRemoved; public event Action? PanChanged; @@ -165,8 +166,22 @@ public void Ungroup(GroupModel group) if (!_groups.Remove(group)) return; + // Todo: batch Refresh() group.Ungroup(); Links.Remove(group.AllLinks.ToArray()); + GroupUngrouped?.Invoke(group); + Refresh(); + } + + public void RemoveGroup(GroupModel group) + { + if (!_groups.Remove(group)) + return; + + // Todo: batch Refresh() + group.Ungroup(); + Nodes.Remove(group.Children); + Links.Remove(group.AllLinks.ToArray()); GroupRemoved?.Invoke(group); Refresh(); } From 7be165bc1a24265b1f39ba08874efd7ae6ee8b22 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 5 Feb 2021 11:08:34 +0100 Subject: [PATCH 04/67] refactor: Selection Selected models are no long kept in memory, SelectionChanged doesn't contain a boolean anymore --- .../Demos/DynamicInsertions.razor.cs | 17 ++++---- samples/SharedDemo/Demos/Events.razor.cs | 4 +- .../Default/DeleteSelectionSubManager.cs | 4 +- .../Default/DragMovablesSubManager.cs | 4 +- .../Default/GroupingSubManager.cs | 8 +--- src/Blazor.Diagrams.Core/DiagramManager.cs | 40 ++++++++----------- 6 files changed, 33 insertions(+), 44 deletions(-) diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs index 6a8099b7..b4448e71 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs @@ -39,11 +39,10 @@ protected void RemoveNode() protected void AddPort() { - var model = diagramManager.SelectedModels.FirstOrDefault(sm => sm is NodeModel); - if (model == null) + var node = diagramManager.Nodes.FirstOrDefault(n => n.Selected); + if (node == null) return; - var node = model as NodeModel; foreach(PortAlignment portAlignment in Enum.GetValues(typeof(PortAlignment))) { if(node.GetPort(portAlignment) == null) @@ -57,11 +56,10 @@ protected void AddPort() protected void RemovePort() { - var model = diagramManager.SelectedModels.FirstOrDefault(sm => sm is NodeModel); - if (model == null) + var node = diagramManager.Nodes.FirstOrDefault(n => n.Selected); + if (node == null) return; - var node = model as NodeModel; if (node.Ports.Count == 0) return; @@ -75,11 +73,12 @@ protected void RemovePort() protected void AddLink() { - if (diagramManager.SelectedModels.Count != 2) + var selectedNodes = diagramManager.Nodes.Where(n => n.Selected).ToArray(); + if (selectedNodes.Length != 2) return; - var node1 = diagramManager.SelectedModels.ElementAt(0) as NodeModel; - var node2 = diagramManager.SelectedModels.ElementAt(1) as NodeModel; + var node1 = selectedNodes[0]; + var node2 = selectedNodes[1]; if (node1 == null || node1.Ports.Count == 0 || node2 == null || node2.Ports.Count == 0) return; diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index 7ec07ff7..af8b2c1b 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -35,9 +35,9 @@ private void RegisterEvents() diagramManager.Nodes.Added += (nodes) => events.AddRange(nodes.Select(n => $"NodesAdded, NodeId={n.Id}")); diagramManager.Nodes.Removed += (nodes) => events.AddRange(nodes.Select(n => $"NodesRemoved, NodeId={n.Id}")); - diagramManager.SelectionChanged += (m, s) => + diagramManager.SelectionChanged += (m) => { - events.Add($"SelectionChanged, Id={m.Id}, Type={m.GetType().Name}, Selected={s}"); + events.Add($"SelectionChanged, Id={m.Id}, Type={m.GetType().Name}, Selected={m.Selected}"); StateHasChanged(); }; diff --git a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs b/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs index adbff1db..9b1d4251 100644 --- a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs +++ b/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs @@ -16,8 +16,8 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) if (e.AltKey || e.CtrlKey || e.ShiftKey || e.Code != DiagramManager.Options.DeleteKey) return; - // TODO: BATCH - foreach (var sm in DiagramManager.SelectedModels.ToList()) + // TODO: BATCH REFRESH + foreach (var sm in DiagramManager.GetSelectedModels().ToList()) { if (sm.Locked) continue; diff --git a/src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs b/src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs index 8bb6cba6..43a84b22 100644 --- a/src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs +++ b/src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs @@ -25,7 +25,7 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) return; // Don't link this linq - _initialPositions = DiagramManager.SelectedModels + _initialPositions = DiagramManager.GetSelectedModels() .Where(m => m is MovableModel) .Select(m => (m as MovableModel)!.Position) .ToArray(); @@ -43,7 +43,7 @@ private void DiagramManager_MouseMove(Model model, MouseEventArgs e) var deltaY = (e.ClientY - _lastClientY.Value) / DiagramManager.Zoom; var i = 0; - foreach (var sm in DiagramManager.SelectedModels) + foreach (var sm in DiagramManager.GetSelectedModels()) { if (!(sm is MovableModel node) || node.Locked) continue; diff --git a/src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs b/src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs index db4e423d..ac6b2e9f 100644 --- a/src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs +++ b/src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs @@ -17,17 +17,13 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) if (!DiagramManager.Options.GroupingEnabled) return; - if (DiagramManager.SelectedModels.Count == 0) + if (!DiagramManager.GetSelectedModels().Any()) return; if (!e.CtrlKey || !e.AltKey || !e.Key.Equals("g", StringComparison.InvariantCultureIgnoreCase)) return; - var selectedNodes = DiagramManager.SelectedModels - .Where(m => m is NodeModel) - .Select(m => (NodeModel)m) - .ToArray(); - + var selectedNodes = DiagramManager.Nodes.Where(n => n.Selected).ToArray(); var nodesWithGroup = selectedNodes.Where(n => n.Group != null).ToArray(); if (nodesWithGroup.Length > 0) { diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 0e7a07eb..db34caa1 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -16,7 +16,6 @@ public class DiagramManager { private readonly List _subManagers; private readonly Dictionary _componentByModelMapping; - private readonly HashSet _selectedModels; private readonly List _groups; public event Action? MouseDown; @@ -26,7 +25,7 @@ public class DiagramManager public event Action? Wheel; public event Action? Changed; - public event Action? SelectionChanged; + public event Action? SelectionChanged; public event Action? GroupAdded; public event Action? GroupUngrouped; public event Action? GroupRemoved; @@ -39,7 +38,6 @@ public DiagramManager(DiagramOptions? options = null) { _subManagers = new List(); _componentByModelMapping = new Dictionary(); - _selectedModels = new HashSet(); _groups = new List(); Options = options ?? new DiagramOptions(); @@ -62,9 +60,6 @@ public DiagramManager(DiagramOptions? options = null) public Layer Nodes { get; } public Layer Links { get; } - public IReadOnlyCollection SelectedModels => _selectedModels; - //public IReadOnlyList Nodes => _nodes; - //public IEnumerable Links => _nodes.SelectMany(n => n.AllLinks).Union(_groups.SelectMany(g => g.AllLinks)).Distinct(); public IReadOnlyList Groups => _groups; public Rectangle? Container { get; internal set; } public Point Pan { get; internal set; } = Point.Zero; @@ -190,45 +185,46 @@ public void RemoveGroup(GroupModel group) #region Selection + public IEnumerable GetSelectedModels() + { + var selectedNodes = Nodes.Where(n => n.Selected); + var selectedLinks = Links.Where(n => n.Selected).Cast(); + var selectedGroups = Groups.Where(n => n.Selected); + return selectedNodes.Union(selectedLinks.Union(selectedGroups)); + } + public void SelectModel(SelectableModel model, bool unselectOthers) { - if (_selectedModels.Contains(model)) + if (model.Selected) return; if (unselectOthers) UnselectAll(); model.Selected = true; - _selectedModels.Add(model); model.Refresh(); - SelectionChanged?.Invoke(model, true); + SelectionChanged?.Invoke(model); } public void UnselectModel(SelectableModel model) { - if (!_selectedModels.Contains(model)) + if (!model.Selected) return; model.Selected = false; - _selectedModels.Remove(model); model.Refresh(); - SelectionChanged?.Invoke(model, false); + SelectionChanged?.Invoke(model); } public void UnselectAll() { - foreach (var model in _selectedModels.ToList()) + foreach (var model in GetSelectedModels()) { - if (!model.Selected) // In case it got unselected by something else, like grouping - continue; - model.Selected = false; model.Refresh(); // Todo: will result in many events, maybe one event for all of them? - SelectionChanged?.Invoke(model, false); + SelectionChanged?.Invoke(model); } - - _selectedModels.Clear(); } #endregion @@ -274,10 +270,10 @@ public void RegisterModelComponent(Type modelType, Type componentType) public void ZoomToFit(double margin = 10) { - if (Nodes.Count == 0) + if (Container == null || Nodes.Count == 0) return; - var selectedNodes = SelectedModels.Where(s => s is NodeModel).Select(s => (NodeModel)s); + var selectedNodes = Nodes.Where(s => s.Selected); (var minX, var maxX, var minY, var maxY) = GetNodesRect(selectedNodes.Any() ? selectedNodes : Nodes); var width = maxX - minX + 2 * margin; var height = maxY - minY + 2 * margin; @@ -291,8 +287,6 @@ public void ZoomToFit(double margin = 10) var nx = Container.Left + Pan.X + minX * Zoom; var ny = Container.Top + Pan.Y + minY * Zoom; UpdatePan(Container.Left - nx, Container.Top - ny); - - Refresh(); } public (double minX, double maxX, double minY, double maxY) GetNodesRect(IEnumerable nodes) From e2beb875e92845423094e17aca77c2e28030c942 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 5 Feb 2021 19:56:45 +0100 Subject: [PATCH 05/67] refactor: SubManagers > Behaviors --- .../{DiagramSubManager.cs => Behavior.cs} | 4 +- .../Behaviors/DebugEventsBehavior.cs | 102 ++++++++++++++++++ .../DeleteSelectionBehavior.cs} | 6 +- .../DragMovablesBehavior.cs} | 6 +- .../DragNewLinkBehavior.cs} | 6 +- .../GroupingBehavior.cs} | 6 +- .../PanBehavior.cs} | 6 +- .../SelectionBehavior.cs} | 6 +- .../ZoomBehavior.cs} | 6 +- src/Blazor.Diagrams.Core/DiagramManager.cs | 45 ++++---- 10 files changed, 149 insertions(+), 44 deletions(-) rename src/Blazor.Diagrams.Core/{DiagramSubManager.cs => Behavior.cs} (65%) create mode 100644 src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs rename src/Blazor.Diagrams.Core/{Default/DeleteSelectionSubManager.cs => Behaviors/DeleteSelectionBehavior.cs} (85%) rename src/Blazor.Diagrams.Core/{Default/DragMovablesSubManager.cs => Behaviors/DragMovablesBehavior.cs} (93%) rename src/Blazor.Diagrams.Core/{Default/DragNewLinkSubManager.cs => Behaviors/DragNewLinkBehavior.cs} (93%) rename src/Blazor.Diagrams.Core/{Default/GroupingSubManager.cs => Behaviors/GroupingBehavior.cs} (89%) rename src/Blazor.Diagrams.Core/{Default/PanSubManager.cs => Behaviors/PanBehavior.cs} (90%) rename src/Blazor.Diagrams.Core/{Default/SelectionSubManager.cs => Behaviors/SelectionBehavior.cs} (83%) rename src/Blazor.Diagrams.Core/{Default/ZoomSubManager.cs => Behaviors/ZoomBehavior.cs} (91%) diff --git a/src/Blazor.Diagrams.Core/DiagramSubManager.cs b/src/Blazor.Diagrams.Core/Behavior.cs similarity index 65% rename from src/Blazor.Diagrams.Core/DiagramSubManager.cs rename to src/Blazor.Diagrams.Core/Behavior.cs index 656d14ed..fd103a02 100644 --- a/src/Blazor.Diagrams.Core/DiagramSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behavior.cs @@ -2,9 +2,9 @@ namespace Blazor.Diagrams.Core { - public abstract class DiagramSubManager : IDisposable + public abstract class Behavior : IDisposable { - public DiagramSubManager(DiagramManager diagramManager) + public Behavior(DiagramManager diagramManager) { DiagramManager = diagramManager; } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs new file mode 100644 index 00000000..257280df --- /dev/null +++ b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs @@ -0,0 +1,102 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; +using System; +using System.Linq; + +namespace Blazor.Diagrams.Core.Behaviors +{ + public class DebugEventsBehavior : Behavior + { + public DebugEventsBehavior(DiagramManager diagramManager) : base(diagramManager) + { + DiagramManager.Changed += DiagramManager_Changed; + DiagramManager.ContainerChanged += DiagramManager_ContainerChanged; + DiagramManager.PanChanged += DiagramManager_PanChanged; + DiagramManager.Nodes.Added += Nodes_Added; + DiagramManager.Nodes.Removed += Nodes_Removed; + DiagramManager.Links.Added += Links_Added; + DiagramManager.Links.Removed += Links_Removed; + DiagramManager.GroupAdded += DiagramManager_GroupAdded; + DiagramManager.GroupRemoved += DiagramManager_GroupRemoved; + DiagramManager.GroupUngrouped += DiagramManager_GroupUngrouped; + DiagramManager.SelectionChanged += DiagramManager_SelectionChanged; + DiagramManager.ZoomChanged += DiagramManager_ZoomChanged; + } + + private void DiagramManager_ZoomChanged() + { + Console.WriteLine($"ZoomChanged, Zoom={DiagramManager.Zoom}"); + } + + private void DiagramManager_SelectionChanged(SelectableModel obj) + { + Console.WriteLine($"SelectionChanged, Model={obj.GetType().Name}, Selected={obj.Selected}"); + } + + private void DiagramManager_GroupUngrouped(GroupModel obj) + { + Console.WriteLine($"GroupUngrouped, Id={obj.Id}"); + } + + private void Links_Removed(LinkModel[] obj) + { + Console.WriteLine($"Links.Removed, Links=[{string.Join(", ", obj.Select(x => x.Id))}]"); + } + + private void Links_Added(LinkModel[] obj) + { + Console.WriteLine($"Links.Added, Links=[{string.Join(", ", obj.Select(x => x.Id))}]"); + } + + private void Nodes_Removed(NodeModel[] obj) + { + Console.WriteLine($"Nodes.Removed, Nodes=[{string.Join(", ", obj.Select(x => x.Id))}]"); + } + + private void Nodes_Added(NodeModel[] obj) + { + Console.WriteLine($"Nodes.Added, Nodes=[{string.Join(", ", obj.Select(x => x.Id))}]"); + } + + private void DiagramManager_PanChanged() + { + Console.WriteLine($"PanChanged, Pan={DiagramManager.Pan}"); + } + + private void DiagramManager_GroupRemoved(GroupModel obj) + { + Console.WriteLine($"GroupRemoved, Id={obj.Id}"); + } + + private void DiagramManager_GroupAdded(GroupModel obj) + { + Console.WriteLine($"GroupAdded, Id={obj.Id}"); + } + + private void DiagramManager_ContainerChanged() + { + Console.WriteLine($"ContainerChanged, Container={DiagramManager.Container}"); + } + + private void DiagramManager_Changed() + { + Console.WriteLine("Changed"); + } + + public override void Dispose() + { + DiagramManager.Changed -= DiagramManager_Changed; + DiagramManager.ContainerChanged -= DiagramManager_ContainerChanged; + DiagramManager.PanChanged -= DiagramManager_PanChanged; + DiagramManager.Nodes.Added -= Nodes_Added; + DiagramManager.Nodes.Removed -= Nodes_Removed; + DiagramManager.Links.Added -= Links_Added; + DiagramManager.Links.Removed -= Links_Removed; + DiagramManager.GroupAdded -= DiagramManager_GroupAdded; + DiagramManager.GroupRemoved -= DiagramManager_GroupRemoved; + DiagramManager.GroupUngrouped -= DiagramManager_GroupUngrouped; + DiagramManager.SelectionChanged -= DiagramManager_SelectionChanged; + DiagramManager.ZoomChanged -= DiagramManager_ZoomChanged; + } + } +} diff --git a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs similarity index 85% rename from src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs index 9b1d4251..e871b9b1 100644 --- a/src/Blazor.Diagrams.Core/Default/DeleteSelectionSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs @@ -2,11 +2,11 @@ using Microsoft.AspNetCore.Components.Web; using System.Linq; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class DeleteSelectionSubManager : DiagramSubManager + public class DeleteSelectionBehavior : Behavior { - public DeleteSelectionSubManager(DiagramManager diagramManager) : base(diagramManager) + public DeleteSelectionBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.KeyDown += DiagramManager_KeyDown; } diff --git a/src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs similarity index 93% rename from src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index 43a84b22..bf034d0f 100644 --- a/src/Blazor.Diagrams.Core/Default/DragMovablesSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -4,15 +4,15 @@ using System; using System.Linq; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class DragMovablesSubManager : DiagramSubManager + public class DragMovablesBehavior : Behavior { private Point[]? _initialPositions; private double? _lastClientX; private double? _lastClientY; - public DragMovablesSubManager(DiagramManager diagramManager) : base(diagramManager) + public DragMovablesBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.MouseDown += DiagramManager_MouseDown; DiagramManager.MouseMove += DiagramManager_MouseMove; diff --git a/src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs similarity index 93% rename from src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index 30f629b8..ab121365 100644 --- a/src/Blazor.Diagrams.Core/Default/DragNewLinkSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -3,15 +3,15 @@ using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class DragNewLinkSubManager : DiagramSubManager + public class DragNewLinkBehavior : Behavior { private double _initialX; private double _initialY; private LinkModel? _ongoingLink; - public DragNewLinkSubManager(DiagramManager diagramManager) : base(diagramManager) + public DragNewLinkBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.MouseDown += DiagramManager_MouseDown; DiagramManager.MouseMove += DiagramManager_MouseMove; diff --git a/src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs similarity index 89% rename from src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs index ac6b2e9f..3592a531 100644 --- a/src/Blazor.Diagrams.Core/Default/GroupingSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs @@ -3,11 +3,11 @@ using System; using System.Linq; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class GroupingSubManager : DiagramSubManager + public class GroupingBehavior : Behavior { - public GroupingSubManager(DiagramManager diagramManager) : base(diagramManager) + public GroupingBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.KeyDown += DiagramManager_KeyDown; } diff --git a/src/Blazor.Diagrams.Core/Default/PanSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs similarity index 90% rename from src/Blazor.Diagrams.Core/Default/PanSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs index 6784a83c..be0e3126 100644 --- a/src/Blazor.Diagrams.Core/Default/PanSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs @@ -2,15 +2,15 @@ using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class PanSubManager : DiagramSubManager + public class PanBehavior : Behavior { private Point? _initialPan; private double _lastClientX; private double _lastClientY; - public PanSubManager(DiagramManager diagramManager) : base(diagramManager) + public PanBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.MouseDown += DiagramManager_MouseDown; DiagramManager.MouseMove += DiagramManager_MouseMove; diff --git a/src/Blazor.Diagrams.Core/Default/SelectionSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs similarity index 83% rename from src/Blazor.Diagrams.Core/Default/SelectionSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs index 709dff62..4446a42d 100644 --- a/src/Blazor.Diagrams.Core/Default/SelectionSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs @@ -1,11 +1,11 @@ using Blazor.Diagrams.Core.Models.Base; using Microsoft.AspNetCore.Components.Web; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class SelectionSubManager : DiagramSubManager + public class SelectionBehavior : Behavior { - public SelectionSubManager(DiagramManager diagramManager) : base(diagramManager) + public SelectionBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.MouseDown += DiagramManager_MouseDown; } diff --git a/src/Blazor.Diagrams.Core/Default/ZoomSubManager.cs b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs similarity index 91% rename from src/Blazor.Diagrams.Core/Default/ZoomSubManager.cs rename to src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs index 33a8f0d9..2d5bb796 100644 --- a/src/Blazor.Diagrams.Core/Default/ZoomSubManager.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs @@ -2,13 +2,13 @@ using Microsoft.AspNetCore.Components.Web; using System; -namespace Blazor.Diagrams.Core.Default +namespace Blazor.Diagrams.Core.Behaviors { - public class ZoomSubManager : DiagramSubManager + public class ZoomBehavior : Behavior { private const float _scaleBy = 1.05f; - public ZoomSubManager(DiagramManager diagramManager) : base(diagramManager) + public ZoomBehavior(DiagramManager diagramManager) : base(diagramManager) { DiagramManager.Wheel += DiagramManager_Wheel; } diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index db34caa1..7634caa1 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Default; +using Blazor.Diagrams.Core.Behaviors; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; @@ -14,7 +14,7 @@ namespace Blazor.Diagrams.Core { public class DiagramManager { - private readonly List _subManagers; + private readonly Dictionary _behaviors; private readonly Dictionary _componentByModelMapping; private readonly List _groups; @@ -36,7 +36,7 @@ public class DiagramManager public DiagramManager(DiagramOptions? options = null) { - _subManagers = new List(); + _behaviors = new Dictionary(); _componentByModelMapping = new Dictionary(); _groups = new List(); @@ -49,13 +49,13 @@ public DiagramManager(DiagramOptions? options = null) Links.Added += OnLinksAdded; Links.Removed += OnLinksRemoved; - RegisterSubManager(); - RegisterSubManager(); - RegisterSubManager(); - RegisterSubManager(); - RegisterSubManager(); - RegisterSubManager(); - RegisterSubManager(); + RegisterBehavior(new SelectionBehavior(this)); + RegisterBehavior(new DragMovablesBehavior(this)); + RegisterBehavior(new DragNewLinkBehavior(this)); + RegisterBehavior(new DeleteSelectionBehavior(this)); + RegisterBehavior(new PanBehavior(this)); + RegisterBehavior(new ZoomBehavior(this)); + RegisterBehavior(new GroupingBehavior(this)); } public Layer Nodes { get; } @@ -229,26 +229,29 @@ public void UnselectAll() #endregion - public void RegisterSubManager() where T : DiagramSubManager + #region Behaviors + + public void RegisterBehavior(Behavior behavior) { - var type = typeof(T); - if (_subManagers.Any(sm => sm.GetType() == type)) - throw new Exception($"SubManager '{type.Name}' already registered."); + var type = behavior.GetType(); + if (_behaviors.ContainsKey(type)) + throw new Exception($"Behavior '{type.Name}' already registered"); - var instance = (DiagramSubManager)Activator.CreateInstance(type, this); - _subManagers.Add(instance); + _behaviors.Add(type, behavior); } - public void UnregisterSubManager() where T : DiagramSubManager + public void UnregisterBehavior() where T : Behavior { - var subManager = _subManagers.FirstOrDefault(sm => sm.GetType() == typeof(T)); - if (subManager == null) + var type = typeof(T); + if (!_behaviors.ContainsKey(type)) return; - subManager.Dispose(); - _subManagers.Remove(subManager); + _behaviors[type].Dispose(); + _behaviors.Remove(type); } + #endregion + public void RegisterModelComponent() where M : Model where C : ComponentBase => RegisterModelComponent(typeof(M), typeof(C)); From 933236c0ae09d0331c1175b6ba9d19d5e785c11f Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sun, 7 Feb 2021 23:50:03 +0100 Subject: [PATCH 06/67] feat: Routers & PathGenerators --- src/Blazor.Diagrams.Core/Delegates.cs | 9 ++++ src/Blazor.Diagrams.Core/DiagramManager.cs | 7 ++- src/Blazor.Diagrams.Core/DiagramOptions.cs | 8 +-- src/Blazor.Diagrams.Core/Models/LinkModel.cs | 10 +--- src/Blazor.Diagrams.Core/Models/PortModel.cs | 1 + .../PathGenerators/PathGenerators.Smooth.cs | 53 +++++++++++++++++++ .../PathGenerators/PathGenerators.Straight.cs | 22 ++++++++ .../Routers/Routers.Normal.cs | 11 ++++ .../Components/DiagramCanvas.razor | 3 +- .../Components/LinkWidget.razor | 49 +++++------------ 10 files changed, 121 insertions(+), 52 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Delegates.cs create mode 100644 src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs create mode 100644 src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs create mode 100644 src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs new file mode 100644 index 00000000..bbbef3cc --- /dev/null +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -0,0 +1,9 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Core +{ + public delegate Point[] Router(DiagramManager diagram, LinkModel link, Point from, Point to); + + public delegate string PathGenerator(DiagramManager diagram, LinkModel link, Point[] route); +} diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 7634caa1..5813f6b3 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -347,7 +347,12 @@ internal void SetContainer(Rectangle newRect) } public Point GetRelativePoint(double clientX, double clientY) - => new Point((clientX - Container.Left - Pan.X) / Zoom, (clientY - Container.Top - Pan.Y) / Zoom); + { + if (Container == null) + throw new Exception("Container not available. Make sure you're not using this method before the diagram is fully loaded"); + + return new Point((clientX - Container.Left - Pan.X) / Zoom, (clientY - Container.Top - Pan.Y) / Zoom); + } internal void OnMouseDown(Model model, MouseEventArgs e) => MouseDown?.Invoke(model, e); diff --git a/src/Blazor.Diagrams.Core/DiagramOptions.cs b/src/Blazor.Diagrams.Core/DiagramOptions.cs index 6491041c..9722df84 100644 --- a/src/Blazor.Diagrams.Core/DiagramOptions.cs +++ b/src/Blazor.Diagrams.Core/DiagramOptions.cs @@ -27,16 +27,16 @@ public class DiagramOptions public class DiagramLinkOptions { - [Description("The default type of newly created links")] - public LinkType DefaultLinkType { get; set; } [Description("The default component for links")] public Type? DefaultLinkComponent { get; set; } - [Description("The default link model")] - public Type? DefaultLinkModel { get; set; } [Description("The default color for links")] public string DefaultColor { get; set; } = "black"; [Description("The default color for selected links")] public string DefaultSelectedColor { get; set; } = "rgb(110, 159, 212)"; + [Description("Default Router for links")] + public Router DefaultRouter { get; set; } = Routers.Normal; + [Description("Default PathGenerator for links")] + public PathGenerator DefaultPathGenerator { get; set; } = PathGenerators.Smooth; } public class DiagramZoomOptions diff --git a/src/Blazor.Diagrams.Core/Models/LinkModel.cs b/src/Blazor.Diagrams.Core/Models/LinkModel.cs index 851e874a..f80f1f85 100644 --- a/src/Blazor.Diagrams.Core/Models/LinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/LinkModel.cs @@ -21,11 +21,12 @@ public LinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) TargetPort = targetPort; } - public LinkType Type { get; set; } public PortModel SourcePort { get; private set; } public PortModel? TargetPort { get; private set; } public bool IsAttached => TargetPort != null; public Point? OnGoingPosition { get; set; } + public Router? Router { get; set; } + public PathGenerator? PathGenerator { get; set; } public void SetSourcePort(PortModel port) { @@ -49,11 +50,4 @@ public void SetTargetPort(PortModel port) TargetPortChanged?.Invoke(); } } - - public enum LinkType - { - Curved, - Line, - LineWithArrowToTarget - } } diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index 06b5c5c9..8b6808e1 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -30,6 +30,7 @@ public PortModel(string id, NodeModel parent, PortAlignment alignment = PortAlig public NodeModel Parent { get; } public PortAlignment Alignment { get; } public Point Position { get; set; } + public Point MiddlePosition => new Point(Position.X + Size.Width / 2, Position.Y + Size.Height / 2); public Size Size { get; set; } public ReadOnlyCollection Links => _links.AsReadOnly(); public bool Initialized { get; internal set; } diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs new file mode 100644 index 00000000..24ebca74 --- /dev/null +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -0,0 +1,53 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; +using System; +using System.Text; + +namespace Blazor.Diagrams.Core +{ + public static partial class PathGenerators + { + private const double _margin = 125; + + public static string Smooth(DiagramManager _, LinkModel link, Point[] route) + { + var sb = new StringBuilder(FormattableString.Invariant($"M {route[0].X} {route[0].Y}")); + + for (var i = 1; i < route.Length; i++) + { + // Todo: alignments should be null for middle segments + sb.Append(GenerateCurvedPath(route[i - 1].X, route[i - 1].Y, route[i].X, route[i].Y, + link.SourcePort.Alignment, link.TargetPort?.Alignment)); + } + + return sb.ToString(); + } + + private static string GenerateCurvedPath(double sX, double sY, double tX, double tY, + PortAlignment sourcePortAlignment, PortAlignment? targetPortAlignment) + { + var cX = (sX + tX) / 2; + var cY = (sY + tY) / 2; + var curvePointA = GetCurvePoint(sX, sY, cX, cY, sourcePortAlignment); + var curvePointB = GetCurvePoint(tX, tY, cX, cY, targetPortAlignment); + return FormattableString.Invariant($" C {curvePointA}, {curvePointB}, {tX} {tY}"); + } + + private static string GetCurvePoint(double pX, double pY, double cX, double cY, PortAlignment? alignment) + { + var margin = Math.Min(_margin, Math.Pow(Math.Pow(pX - cX, 2) + Math.Pow(pY - cY, 2), .5)); + return alignment switch + { + PortAlignment.Top => FormattableString.Invariant($"{pX} {Math.Min(pY - margin, cY)}"), + PortAlignment.Bottom => FormattableString.Invariant($"{pX} {Math.Max(pY + margin, cY)}"), + PortAlignment.TopRight => FormattableString.Invariant($"{Math.Max(pX + margin, cX)} {Math.Min(pY - margin, cY)}"), + PortAlignment.BottomRight => FormattableString.Invariant($"{Math.Max(pX + margin, cX)} {Math.Max(pY + margin, cY)}"), + PortAlignment.Right => FormattableString.Invariant($"{Math.Max(pX + margin, cX)} {pY}"), + PortAlignment.Left => FormattableString.Invariant($"{Math.Min(pX - margin, cX)} {pY}"), + PortAlignment.BottomLeft => FormattableString.Invariant($"{Math.Min(pX - margin, cX)} {Math.Max(pY + margin, cY)}"), + PortAlignment.TopLeft => FormattableString.Invariant($"{Math.Min(pX - margin, cX)} {Math.Min(pY - margin, cY)}"), + _ => FormattableString.Invariant($"{cX} {cY}"), + }; + } + } +} diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs new file mode 100644 index 00000000..1d7f682a --- /dev/null +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs @@ -0,0 +1,22 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; +using System; +using System.Text; + +namespace Blazor.Diagrams.Core +{ + public static partial class PathGenerators + { + public static string Straight(DiagramManager _, LinkModel link, Point[] route) + { + var sb = new StringBuilder(FormattableString.Invariant($"M {route[0].X} {route[0].Y}")); + + for (var i = 1; i < route.Length; i++) + { + sb.Append(FormattableString.Invariant($" L {route[i].X} {route[i].Y}")); + } + + return sb.ToString(); + } + } +} diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs new file mode 100644 index 00000000..6179751e --- /dev/null +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs @@ -0,0 +1,11 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Core +{ + public static partial class Routers + { + public static Point[] Normal(DiagramManager _, LinkModel link, Point from, Point to) + => new[] { from, to }; + } +} diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index 0b659860..c1f534fc 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -12,8 +12,7 @@ @onwheel:stopPropagation> @* Links *@ - + @foreach (var link in DiagramManager.Links) { @link.Id diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index 79743921..ba3f73b7 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -6,41 +6,16 @@ public LinkModel Link { get; set; } } -@* Maybe we can use markers? *@ +@{ + var router = Link.Router ?? DiagramManager.Options.Links.DefaultRouter; + var pathGenerator = Link.PathGenerator ?? DiagramManager.Options.Links.DefaultPathGenerator; + var route = router(DiagramManager, Link, Link.SourcePort.MiddlePosition, Link.TargetPort?.MiddlePosition ?? Link.OnGoingPosition); + var path = pathGenerator(DiagramManager, Link, route); +} -@switch (Link.Type) -{ - case Core.Models.LinkType.Line: - - break; - case Core.Models.LinkType.Curved: - - break; - case Core.Models.LinkType.LineWithArrowToTarget: - - - - - - - - - - - - break; -} \ No newline at end of file + \ No newline at end of file From dfec3d90b4d4d9c0915e7fa1187e3148bcc178df Mon Sep 17 00:00:00 2001 From: zHaytam Date: Wed, 10 Feb 2021 16:00:28 +0100 Subject: [PATCH 07/67] feat: Link markers --- src/Blazor.Diagrams.Core/Delegates.cs | 2 +- src/Blazor.Diagrams.Core/Models/LinkMarker.cs | 31 ++++++++++ src/Blazor.Diagrams.Core/Models/LinkModel.cs | 2 + .../PathGenerators/PathGeneratorResult.cs | 23 ++++++++ .../PathGenerators/PathGenerators.Smooth.cs | 57 +++++++++++-------- .../PathGenerators/PathGenerators.Straight.cs | 18 +++++- .../PathGenerators/PathGenerators.Utils.cs | 26 +++++++++ .../Components/DiagramCanvas.razor | 11 +--- .../Components/DiagramCanvas.razor.cs | 16 +++--- .../Components/LinkWidget.razor | 39 +++++++------ .../Components/LinkWidget.razor.cs | 44 ++++++++++++++ src/Blazor.Diagrams/_Imports.razor | 2 +- 12 files changed, 209 insertions(+), 62 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Models/LinkMarker.cs create mode 100644 src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs create mode 100644 src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs create mode 100644 src/Blazor.Diagrams/Components/LinkWidget.razor.cs diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs index bbbef3cc..9c2969ac 100644 --- a/src/Blazor.Diagrams.Core/Delegates.cs +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -5,5 +5,5 @@ namespace Blazor.Diagrams.Core { public delegate Point[] Router(DiagramManager diagram, LinkModel link, Point from, Point to); - public delegate string PathGenerator(DiagramManager diagram, LinkModel link, Point[] route); + public delegate PathGeneratorResult PathGenerator(DiagramManager diagram, LinkModel link, Point[] route); } diff --git a/src/Blazor.Diagrams.Core/Models/LinkMarker.cs b/src/Blazor.Diagrams.Core/Models/LinkMarker.cs new file mode 100644 index 00000000..dc6b9f47 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Models/LinkMarker.cs @@ -0,0 +1,31 @@ +using System; + +namespace Blazor.Diagrams.Core.Models +{ + public class LinkMarker + { + public static LinkMarker Arrow { get; } = new LinkMarker("M 0 -5 10 0 0 5 z", 10); + public static LinkMarker Circle { get; } = new LinkMarker("M 0, 0 a 5,5 0 1,0 10,0 a 5,5 0 1,0 -10,0", 10); + public static LinkMarker Square { get; } = new LinkMarker("M 0 -5 10 -5 10 5 0 5 z", 10); + + public LinkMarker(string path, double width) + { + Path = path; + Width = width; + } + + public string Path { get; } + public double Width { get; } + + public static LinkMarker NewArrow(double width, double height) + => new LinkMarker(FormattableString.Invariant($"M 0 -{height / 2} {width} 0 0 {height / 2}"), width); + + public static LinkMarker NewCircle(double r) + => new LinkMarker(FormattableString.Invariant($"M 0, 0 a {r},{r} 0 1,0 {r * 2},0 a {r},{r} 0 1,0 -{r * 2},0"), r * 2); + + public static LinkMarker NewRectangle(double width, double height) + => new LinkMarker(FormattableString.Invariant($"M 0 -{height / 2} {width} -{height / 2} {width} {height / 2} 0 {height / 2} z"), width); + + public static LinkMarker NewSquare(double size) => NewRectangle(size, size); + } +} diff --git a/src/Blazor.Diagrams.Core/Models/LinkModel.cs b/src/Blazor.Diagrams.Core/Models/LinkModel.cs index f80f1f85..5704fa0e 100644 --- a/src/Blazor.Diagrams.Core/Models/LinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/LinkModel.cs @@ -27,6 +27,8 @@ public LinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) public Point? OnGoingPosition { get; set; } public Router? Router { get; set; } public PathGenerator? PathGenerator { get; set; } + public LinkMarker? SourceMarker { get; set; } + public LinkMarker? TargetMarker { get; set; } public void SetSourcePort(PortModel port) { diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs new file mode 100644 index 00000000..ebe4b0ee --- /dev/null +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs @@ -0,0 +1,23 @@ +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Core +{ + public class PathGeneratorResult + { + public PathGeneratorResult(string path, double? sourceMarkerAngle = null, Point? sourceMarkerPosition = null, + double? targetMarkerAngle = null, Point? targetMarkerPosition = null) + { + Path = path; + SourceMarkerAngle = sourceMarkerAngle; + SourceMarkerPosition = sourceMarkerPosition; + TargetMarkerAngle = targetMarkerAngle; + TargetMarkerPosition = targetMarkerPosition; + } + + public string Path { get; } + public double? SourceMarkerAngle { get; } + public Point? SourceMarkerPosition { get; } + public double? TargetMarkerAngle { get; } + public Point? TargetMarkerPosition { get; } + } +} diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs index 24ebca74..589361e2 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -1,7 +1,6 @@ using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Core; using System; -using System.Text; namespace Blazor.Diagrams.Core { @@ -9,44 +8,52 @@ public static partial class PathGenerators { private const double _margin = 125; - public static string Smooth(DiagramManager _, LinkModel link, Point[] route) + public static PathGeneratorResult Smooth(DiagramManager _, LinkModel link, Point[] route) { - var sb = new StringBuilder(FormattableString.Invariant($"M {route[0].X} {route[0].Y}")); + if (route.Length > 2) + throw new NotImplementedException(); // Curved path through points - for (var i = 1; i < route.Length; i++) + route = GetRouteWithCurvePoints(link, route); + double? sourceAngle = null; + double? targetAngle = null; + + if (link.SourceMarker != null) + { + sourceAngle = SourceMarkerAdjustement(route, link.SourceMarker.Width); + } + + if (link.TargetMarker != null) { - // Todo: alignments should be null for middle segments - sb.Append(GenerateCurvedPath(route[i - 1].X, route[i - 1].Y, route[i].X, route[i].Y, - link.SourcePort.Alignment, link.TargetPort?.Alignment)); + targetAngle = TargetMarkerAdjustement(route, link.TargetMarker.Width); } - return sb.ToString(); + var path = FormattableString.Invariant($"M {route[0].X} {route[0].Y} C {route[1].X} {route[1].Y}, {route[2].X} {route[2].Y}, {route[3].X} {route[3].Y}"); + return new PathGeneratorResult(path, sourceAngle, route[0], targetAngle, route[^1]); } - private static string GenerateCurvedPath(double sX, double sY, double tX, double tY, - PortAlignment sourcePortAlignment, PortAlignment? targetPortAlignment) + private static Point[] GetRouteWithCurvePoints(LinkModel link, Point[] route) { - var cX = (sX + tX) / 2; - var cY = (sY + tY) / 2; - var curvePointA = GetCurvePoint(sX, sY, cX, cY, sourcePortAlignment); - var curvePointB = GetCurvePoint(tX, tY, cX, cY, targetPortAlignment); - return FormattableString.Invariant($" C {curvePointA}, {curvePointB}, {tX} {tY}"); + var cX = (route[0].X + route[1].X) / 2; + var cY = (route[0].Y + route[1].Y) / 2; + var curvePointA = GetCurvePoint(route[0].X, route[0].Y, cX, cY, link.SourcePort.Alignment); + var curvePointB = GetCurvePoint(route[1].X, route[1].Y, cX, cY, link.TargetPort?.Alignment); + return new[] { route[0], curvePointA, curvePointB, route[1] }; } - private static string GetCurvePoint(double pX, double pY, double cX, double cY, PortAlignment? alignment) + private static Point GetCurvePoint(double pX, double pY, double cX, double cY, PortAlignment? alignment) { var margin = Math.Min(_margin, Math.Pow(Math.Pow(pX - cX, 2) + Math.Pow(pY - cY, 2), .5)); return alignment switch { - PortAlignment.Top => FormattableString.Invariant($"{pX} {Math.Min(pY - margin, cY)}"), - PortAlignment.Bottom => FormattableString.Invariant($"{pX} {Math.Max(pY + margin, cY)}"), - PortAlignment.TopRight => FormattableString.Invariant($"{Math.Max(pX + margin, cX)} {Math.Min(pY - margin, cY)}"), - PortAlignment.BottomRight => FormattableString.Invariant($"{Math.Max(pX + margin, cX)} {Math.Max(pY + margin, cY)}"), - PortAlignment.Right => FormattableString.Invariant($"{Math.Max(pX + margin, cX)} {pY}"), - PortAlignment.Left => FormattableString.Invariant($"{Math.Min(pX - margin, cX)} {pY}"), - PortAlignment.BottomLeft => FormattableString.Invariant($"{Math.Min(pX - margin, cX)} {Math.Max(pY + margin, cY)}"), - PortAlignment.TopLeft => FormattableString.Invariant($"{Math.Min(pX - margin, cX)} {Math.Min(pY - margin, cY)}"), - _ => FormattableString.Invariant($"{cX} {cY}"), + PortAlignment.Top => new Point(pX, Math.Min(pY - margin, cY)), + PortAlignment.Bottom => new Point(pX, Math.Max(pY + margin, cY)), + PortAlignment.TopRight => new Point(Math.Max(pX + margin, cX), Math.Min(pY - margin, cY)), + PortAlignment.BottomRight => new Point(Math.Max(pX + margin, cX), Math.Max(pY + margin, cY)), + PortAlignment.Right => new Point(Math.Max(pX + margin, cX), pY), + PortAlignment.Left => new Point(Math.Min(pX - margin, cX), pY), + PortAlignment.BottomLeft => new Point(Math.Min(pX - margin, cX), Math.Max(pY + margin, cY)), + PortAlignment.TopLeft => new Point(Math.Min(pX - margin, cX), Math.Min(pY - margin, cY)), + _ => new Point(cX, cY), }; } } diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs index 1d7f682a..addb4ad4 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs @@ -7,8 +7,22 @@ namespace Blazor.Diagrams.Core { public static partial class PathGenerators { - public static string Straight(DiagramManager _, LinkModel link, Point[] route) + public static PathGeneratorResult Straight(DiagramManager _, LinkModel link, Point[] route) { + route = (Point[])route.Clone(); + double? sourceAngle = null; + double? targetAngle = null; + + if (link.SourceMarker != null) + { + sourceAngle = SourceMarkerAdjustement(route, link.SourceMarker.Width); + } + + if (link.TargetMarker != null) + { + targetAngle = TargetMarkerAdjustement(route, link.TargetMarker.Width); + } + var sb = new StringBuilder(FormattableString.Invariant($"M {route[0].X} {route[0].Y}")); for (var i = 1; i < route.Length; i++) @@ -16,7 +30,7 @@ public static string Straight(DiagramManager _, LinkModel link, Point[] route) sb.Append(FormattableString.Invariant($" L {route[i].X} {route[i].Y}")); } - return sb.ToString(); + return new PathGeneratorResult(sb.ToString(), sourceAngle, route[0], targetAngle, route[^1]); } } } diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs new file mode 100644 index 00000000..e836257c --- /dev/null +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs @@ -0,0 +1,26 @@ +using Blazor.Diagrams.Core.Models.Core; +using System; + +namespace Blazor.Diagrams.Core +{ + public static partial class PathGenerators + { + public static double SourceMarkerAdjustement(Point[] route, double markerWidth) + { + var angleInRadians = Math.Atan2(route[1].Y - route[0].Y, route[1].X - route[0].X) + Math.PI; + var xChange = markerWidth * Math.Cos(angleInRadians); + var yChange = markerWidth * Math.Sin(angleInRadians); + route[0] = new Point(route[0].X - xChange, route[0].Y - yChange); + return angleInRadians * 180 / Math.PI; + } + + public static double TargetMarkerAdjustement(Point[] route, double markerWidth) + { + var angleInRadians = Math.Atan2(route[^1].Y - route[^2].Y, route[^1].X - route[^2].X); + var xChange = markerWidth * Math.Cos(angleInRadians); + var yChange = markerWidth * Math.Sin(angleInRadians); + route[^1] = new Point(route[^1].X - xChange, route[^1].Y - yChange); + return angleInRadians * 180 / Math.PI; + } + } +} diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index c1f534fc..0c6a8eda 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -1,6 +1,4 @@ -@inherits DiagramCanvasComponent - -
@* Links *@ - + @foreach (var link in DiagramManager.Links) { - @link.Id } @@ -26,9 +23,7 @@ @* Nodes *@ -
- +
@foreach (var group in DiagramManager.Groups) { diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs index 52c143da..7a254674 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs @@ -5,12 +5,11 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.JSInterop; using System; -using System.Globalization; using System.Threading.Tasks; namespace Blazor.Diagrams.Components { - public class DiagramCanvasComponent : ComponentBase, IDisposable + public partial class DiagramCanvas : IDisposable { [CascadingParameter(Name = "DiagramManager")] public DiagramManager DiagramManager { get; set; } @@ -25,19 +24,18 @@ public class DiagramCanvasComponent : ComponentBase, IDisposable public IJSRuntime JSRuntime { get; set; } protected ElementReference elementReference; - private DotNetObjectReference _reference; + private DotNetObjectReference _reference; private bool _shouldReRender; - public string PanX => DiagramManager.Pan.X.ToString(CultureInfo.InvariantCulture); - public string PanY => DiagramManager.Pan.Y.ToString(CultureInfo.InvariantCulture); - public string Zoom => DiagramManager.Zoom.ToString(CultureInfo.InvariantCulture); + private string LayerStyle + => FormattableString.Invariant($"transform: translate({DiagramManager.Pan.X}px, {DiagramManager.Pan.Y}px) scale({DiagramManager.Zoom});"); protected override void OnInitialized() { base.OnInitialized(); _reference = DotNetObjectReference.Create(this); - DiagramManager.Changed += DiagramManager_Changed; + DiagramManager.Changed += OnDiagramChanged; } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -75,7 +73,7 @@ protected override bool ShouldRender() protected void OnWheel(WheelEventArgs e) => DiagramManager.OnWheel(e); - private void DiagramManager_Changed() + private void OnDiagramChanged() { _shouldReRender = true; StateHasChanged(); @@ -83,7 +81,7 @@ private void DiagramManager_Changed() public void Dispose() { - DiagramManager.Changed -= DiagramManager_Changed; + DiagramManager.Changed -= OnDiagramChanged; if (_reference == null) return; diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index ba3f73b7..08b2e455 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -1,21 +1,28 @@ -@code { - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } - - [Parameter] - public LinkModel Link { get; set; } -} - -@{ +@{ var router = Link.Router ?? DiagramManager.Options.Links.DefaultRouter; var pathGenerator = Link.PathGenerator ?? DiagramManager.Options.Links.DefaultPathGenerator; - var route = router(DiagramManager, Link, Link.SourcePort.MiddlePosition, Link.TargetPort?.MiddlePosition ?? Link.OnGoingPosition); - var path = pathGenerator(DiagramManager, Link, route); + var sourcePosition = GetPositionBasedOnAlignment(Link.SourcePort, Link.SourceMarker); + var targetPosition = Link.IsAttached ? GetPositionBasedOnAlignment(Link.TargetPort, Link.TargetMarker) : Link.OnGoingPosition; + var route = router(DiagramManager, Link, sourcePosition, targetPosition); + var result = pathGenerator(DiagramManager, Link, route); + var color = Link.Selected ? DiagramManager.Options.Links.DefaultSelectedColor : DiagramManager.Options.Links.DefaultColor; } - \ No newline at end of file + stroke="@color" /> + +@if (Link.SourceMarker != null && result.SourceMarkerAngle != null && result.SourceMarkerPosition != null) +{ + + + +} + +@if (Link.TargetMarker != null && result.TargetMarkerAngle != null && result.TargetMarkerPosition != null) +{ + + + +} \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs new file mode 100644 index 00000000..19c4a46d --- /dev/null +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -0,0 +1,44 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core; +using Microsoft.AspNetCore.Components; +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Components +{ + public partial class LinkWidget + { + + [CascadingParameter(Name = "DiagramManager")] + public DiagramManager DiagramManager { get; set; } + + [Parameter] + public LinkModel Link { get; set; } + + private Point GetPositionBasedOnAlignment(PortModel port, LinkMarker marker) + { + if (marker == null) + return port.MiddlePosition; + + var pt = port.Position; + switch (port.Alignment) + { + case PortAlignment.Top: + return new Point(pt.X + port.Size.Width / 2, pt.Y); + case PortAlignment.TopRight: + return new Point(pt.X + port.Size.Width, pt.Y); + case PortAlignment.Right: + return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height / 2); + case PortAlignment.BottomRight: + return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height); + case PortAlignment.Bottom: + return new Point(pt.X + port.Size.Width / 2, pt.Y + port.Size.Height); + case PortAlignment.BottomLeft: + return new Point(pt.X, pt.Y + port.Size.Height); + case PortAlignment.Left: + return new Point(pt.X, pt.Y + port.Size.Height / 2); + default: + return pt; + } + } + } +} diff --git a/src/Blazor.Diagrams/_Imports.razor b/src/Blazor.Diagrams/_Imports.razor index 041dae76..8ec66edd 100644 --- a/src/Blazor.Diagrams/_Imports.razor +++ b/src/Blazor.Diagrams/_Imports.razor @@ -3,4 +3,4 @@ @using Blazor.Diagrams.Core.Extensions; @using Blazor.Diagrams.Core.Models; @using Blazor.Diagrams.Components.Renderers -@using Blazor.Diagrams.Core.Models.Core; \ No newline at end of file +@using Blazor.Diagrams.Core.Models.Core; From 90df536f7b1c5751aea15354df12db1cdbf47a26 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Wed, 10 Feb 2021 21:47:46 +0100 Subject: [PATCH 08/67] feat: MouseClick event --- samples/SharedDemo/Demos/Events.razor.cs | 6 +++ .../Behaviors/EventsBehavior.cs | 51 +++++++++++++++++++ src/Blazor.Diagrams.Core/DiagramManager.cs | 4 ++ 3 files changed, 61 insertions(+) create mode 100644 src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index af8b2c1b..64386ce9 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -57,6 +57,12 @@ private void RegisterEvents() events.Add($"MouseUp, Type={m?.GetType().Name}, ModelId={m?.Id}"); StateHasChanged(); }; + + diagramManager.MouseClick += (m, e) => + { + events.Add($"MouseClick, Type={m?.GetType().Name}, ModelId={m?.Id}"); + StateHasChanged(); + }; } private NodeModel NewNode(double x, double y) diff --git a/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs new file mode 100644 index 00000000..349c1db8 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs @@ -0,0 +1,51 @@ +using Blazor.Diagrams.Core.Models.Base; +using Microsoft.AspNetCore.Components.Web; +using System; + +namespace Blazor.Diagrams.Core.Behaviors +{ + public class EventsBehavior : Behavior + { + private bool _captureMouseMove; + private int _mouseMovedCount; + + public EventsBehavior(DiagramManager diagramManager) : base(diagramManager) + { + DiagramManager.MouseDown += OnMouseDown; + DiagramManager.MouseMove += OnMouseMove; + DiagramManager.MouseUp += OnMouseUp; + } + + private void OnMouseDown(Model model, MouseEventArgs e) + { + _captureMouseMove = true; + } + + private void OnMouseMove(Model model, MouseEventArgs e) + { + if (!_captureMouseMove) + return; + + _mouseMovedCount++; + } + + private void OnMouseUp(Model model, MouseEventArgs e) + { + _captureMouseMove = false; + if (_mouseMovedCount > 0) + { + _mouseMovedCount = 0; + return; + } + + DiagramManager.OnMouseClick(model, e); + } + + public override void Dispose() + { + DiagramManager.MouseDown -= OnMouseDown; + DiagramManager.MouseMove -= OnMouseMove; + DiagramManager.MouseUp -= OnMouseUp; + } + } +} diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 5813f6b3..f4213dd5 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -23,6 +23,7 @@ public class DiagramManager public event Action? MouseUp; public event Action? KeyDown; public event Action? Wheel; + public event Action? MouseClick; public event Action? Changed; public event Action? SelectionChanged; @@ -56,6 +57,7 @@ public DiagramManager(DiagramOptions? options = null) RegisterBehavior(new PanBehavior(this)); RegisterBehavior(new ZoomBehavior(this)); RegisterBehavior(new GroupingBehavior(this)); + RegisterBehavior(new EventsBehavior(this)); } public Layer Nodes { get; } @@ -363,5 +365,7 @@ public Point GetRelativePoint(double clientX, double clientY) internal void OnKeyDown(KeyboardEventArgs e) => KeyDown?.Invoke(e); internal void OnWheel(WheelEventArgs e) => Wheel?.Invoke(e); + + internal void OnMouseClick(Model model, MouseEventArgs e) => MouseClick?.Invoke(model, e); } } From 1b827b919df920819473dfacfb4c86ad4fbd8a1b Mon Sep 17 00:00:00 2001 From: zHaytam Date: Thu, 11 Feb 2021 11:55:13 +0100 Subject: [PATCH 09/67] feat: Add data-x-id on nodes, ports and links --- .../Components/Renderers/LinkRenderer.cs | 13 ++++++------ .../Components/Renderers/NodeRenderer.cs | 20 +++++++++---------- .../Components/Renderers/PortRenderer.razor | 1 + 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs index e483db42..ca42decf 100644 --- a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs @@ -39,12 +39,13 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.OpenElement(0, "g"); builder.AddAttribute(1, "class", "link"); - builder.AddAttribute(2, "onmousedown", EventCallback.Factory.Create(this, OnMouseDown)); - builder.AddEventStopPropagationAttribute(3, "onmousedown", true); - builder.AddAttribute(4, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); - builder.AddEventStopPropagationAttribute(5, "onmouseup", true); - builder.OpenComponent(6, componentType); - builder.AddAttribute(7, "Link", Link); + builder.AddAttribute(2, "data-link-id", Link.Id); + builder.AddAttribute(3, "onmousedown", EventCallback.Factory.Create(this, OnMouseDown)); + builder.AddEventStopPropagationAttribute(4, "onmousedown", true); + builder.AddAttribute(5, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); + builder.AddEventStopPropagationAttribute(6, "onmouseup", true); + builder.OpenComponent(7, componentType); + builder.AddAttribute(8, "Link", Link); builder.CloseComponent(); builder.CloseElement(); } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index 38c6f938..46dcf033 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -93,24 +93,24 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.OpenElement(0, Node.Layer == RenderLayer.HTML ? "div" : "g"); builder.AddAttribute(1, "class", $"node{(Node.Locked ? " locked" : string.Empty)}"); - builder.AddAttribute(55, "data-id", Node.Id); + builder.AddAttribute(2, "data-node-id", Node.Id); if (Node.Layer == RenderLayer.HTML) { - builder.AddAttribute(2, "style", $"top: {Node.Position.Y.ToInvariantString()}px; left: {Node.Position.X.ToInvariantString()}px"); + builder.AddAttribute(3, "style", $"top: {Node.Position.Y.ToInvariantString()}px; left: {Node.Position.X.ToInvariantString()}px"); } else { - builder.AddAttribute(2, "transform", $"translate({Node.Position.X.ToInvariantString()} {Node.Position.Y.ToInvariantString()})"); + builder.AddAttribute(3, "transform", $"translate({Node.Position.X.ToInvariantString()} {Node.Position.Y.ToInvariantString()})"); } - builder.AddAttribute(3, "onmousedown", EventCallback.Factory.Create(this, OnMouseDown)); - builder.AddEventStopPropagationAttribute(4, "onmousedown", true); - builder.AddAttribute(5, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); - builder.AddEventStopPropagationAttribute(6, "onmouseup", true); - builder.AddElementReferenceCapture(7, value => _element = value); - builder.OpenComponent(8, componentType); - builder.AddAttribute(9, "Node", Node); + builder.AddAttribute(4, "onmousedown", EventCallback.Factory.Create(this, OnMouseDown)); + builder.AddEventStopPropagationAttribute(5, "onmousedown", true); + builder.AddAttribute(6, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); + builder.AddEventStopPropagationAttribute(7, "onmouseup", true); + builder.AddElementReferenceCapture(8, value => _element = value); + builder.OpenComponent(9, componentType); + builder.AddAttribute(10, "Node", Node); builder.CloseComponent(); builder.CloseElement(); } diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor index 61bd7b4d..9563eed5 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor @@ -1,4 +1,5 @@ 
Date: Sat, 13 Feb 2021 18:03:16 +0100 Subject: [PATCH 10/67] refactor: Introduce abstract BaseLinkModel + add Color/SelectedColor/Width to LinkModel --- .../Behaviors/DebugEventsBehavior.cs | 4 +- .../Behaviors/DeleteSelectionBehavior.cs | 3 +- .../Behaviors/DragNewLinkBehavior.cs | 3 +- src/Blazor.Diagrams.Core/Delegates.cs | 6 +-- src/Blazor.Diagrams.Core/DiagramManager.cs | 8 +-- src/Blazor.Diagrams.Core/DiagramOptions.cs | 3 +- ...tensions.cs => BaseLinkModelExtensions.cs} | 23 ++++---- .../Models/Base/BaseLinkModel.cs | 54 +++++++++++++++++++ src/Blazor.Diagrams.Core/Models/GroupModel.cs | 5 +- src/Blazor.Diagrams.Core/Models/LinkModel.cs | 52 +++--------------- src/Blazor.Diagrams.Core/Models/NodeModel.cs | 2 +- src/Blazor.Diagrams.Core/Models/PortModel.cs | 8 +-- .../PathGenerators/PathGenerators.Smooth.cs | 5 +- .../PathGenerators/PathGenerators.Straight.cs | 4 +- .../Routers/Routers.Normal.cs | 4 +- .../Components/LinkWidget.razor | 5 +- .../Components/Renderers/LinkRenderer.cs | 5 +- 17 files changed, 107 insertions(+), 87 deletions(-) rename src/Blazor.Diagrams.Core/Extensions/{LinkModelExtensions.cs => BaseLinkModelExtensions.cs} (83%) create mode 100644 src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs diff --git a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs index 257280df..1d2376b7 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs @@ -38,12 +38,12 @@ private void DiagramManager_GroupUngrouped(GroupModel obj) Console.WriteLine($"GroupUngrouped, Id={obj.Id}"); } - private void Links_Removed(LinkModel[] obj) + private void Links_Removed(BaseLinkModel[] obj) { Console.WriteLine($"Links.Removed, Links=[{string.Join(", ", obj.Select(x => x.Id))}]"); } - private void Links_Added(LinkModel[] obj) + private void Links_Added(BaseLinkModel[] obj) { Console.WriteLine($"Links.Added, Links=[{string.Join(", ", obj.Select(x => x.Id))}]"); } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs index e871b9b1..6435eb0d 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs @@ -1,4 +1,5 @@ using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Microsoft.AspNetCore.Components.Web; using System.Linq; @@ -30,7 +31,7 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) { DiagramManager.Nodes.Remove(node); } - else if (sm is LinkModel link) + else if (sm is BaseLinkModel link) { DiagramManager.Links.Remove(link); } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index ab121365..fd506dc5 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -9,7 +9,7 @@ public class DragNewLinkBehavior : Behavior { private double _initialX; private double _initialY; - private LinkModel? _ongoingLink; + private BaseLinkModel? _ongoingLink; public DragNewLinkBehavior(DiagramManager diagramManager) : base(diagramManager) { @@ -25,6 +25,7 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) _initialX = e.ClientX; _initialY = e.ClientY; + // Todo: Link creator from Options _ongoingLink = new LinkModel(port, null); _ongoingLink.OnGoingPosition = new Point(port.Position.X + port.Size.Width / 2, port.Position.Y + port.Size.Height / 2); diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs index 9c2969ac..cb4feed9 100644 --- a/src/Blazor.Diagrams.Core/Delegates.cs +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -1,9 +1,9 @@ -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; namespace Blazor.Diagrams.Core { - public delegate Point[] Router(DiagramManager diagram, LinkModel link, Point from, Point to); + public delegate Point[] Router(DiagramManager diagram, BaseLinkModel link, Point from, Point to); - public delegate PathGeneratorResult PathGenerator(DiagramManager diagram, LinkModel link, Point[] route); + public delegate PathGeneratorResult PathGenerator(DiagramManager diagram, BaseLinkModel link, Point[] route); } diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index f4213dd5..3994f480 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -43,7 +43,7 @@ public DiagramManager(DiagramOptions? options = null) Options = options ?? new DiagramOptions(); Nodes = new Layer(); - Links = new Layer(); + Links = new Layer(); Nodes.Added += OnNodesAdded; Nodes.Removed += OnNodesRemoved; @@ -61,7 +61,7 @@ public DiagramManager(DiagramOptions? options = null) } public Layer Nodes { get; } - public Layer Links { get; } + public Layer Links { get; } public IReadOnlyList Groups => _groups; public Rectangle? Container { get; internal set; } public Point Pan { get; internal set; } = Point.Zero; @@ -80,7 +80,7 @@ private void OnNodesRemoved(NodeModel[] nodes) Refresh(); } - private void OnLinksAdded(LinkModel[] links) + private void OnLinksAdded(BaseLinkModel[] links) { foreach (var link in links) { @@ -97,7 +97,7 @@ private void OnLinksAdded(LinkModel[] links) Refresh(); } - private void OnLinksRemoved(LinkModel[] links) + private void OnLinksRemoved(BaseLinkModel[] links) { foreach (var link in links) { diff --git a/src/Blazor.Diagrams.Core/DiagramOptions.cs b/src/Blazor.Diagrams.Core/DiagramOptions.cs index 9722df84..1d05ae7e 100644 --- a/src/Blazor.Diagrams.Core/DiagramOptions.cs +++ b/src/Blazor.Diagrams.Core/DiagramOptions.cs @@ -1,5 +1,4 @@ -using Blazor.Diagrams.Core.Models; -using System; +using System; using System.ComponentModel; namespace Blazor.Diagrams.Core diff --git a/src/Blazor.Diagrams.Core/Extensions/LinkModelExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs similarity index 83% rename from src/Blazor.Diagrams.Core/Extensions/LinkModelExtensions.cs rename to src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs index b5bba5a4..98e6b830 100644 --- a/src/Blazor.Diagrams.Core/Extensions/LinkModelExtensions.cs +++ b/src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs @@ -1,10 +1,11 @@ using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using System; using System.Globalization; namespace Blazor.Diagrams.Core.Extensions { - public static class LinkModelExtensions + public static class BaseLinkModelExtensions { private const double _margin = 125; @@ -12,8 +13,8 @@ public static class LinkModelExtensions /// If the link is attached, returns the same output as GetMiddleTargetX(). /// Otherwise, returns the X value of link's ongoing position. ///
- /// The LinkModel entity - public static double GetTargetX(this LinkModel link) + /// The BaseLinkModel entity + public static double GetTargetX(this BaseLinkModel link) { if (!link.IsAttached) return link.OnGoingPosition!.X; @@ -25,8 +26,8 @@ public static double GetTargetX(this LinkModel link) /// If the link is attached, returns the same output as GetMiddleTargetY(). /// Otherwise, returns the Y value of link's ongoing position. /// - /// The LinkModel entity - public static double GetTargetY(this LinkModel link) + /// The BaseLinkModel entity + public static double GetTargetY(this BaseLinkModel link) { if (!link.IsAttached) return link.OnGoingPosition!.Y; @@ -34,7 +35,7 @@ public static double GetTargetY(this LinkModel link) return link.GetMiddleTargetY(); } - public static string GenerateCurvedPath(this LinkModel link) + public static string GenerateCurvedPath(this BaseLinkModel link) { var sX = link.GetMiddleSourceX(); var sY = link.GetMiddleSourceY(); @@ -76,7 +77,7 @@ private static string GetCurvePoint(double pX, double pY, double cX, double cY, }; } - public static string CalculateAngleForTargetArrow(this LinkModel link) + public static string CalculateAngleForTargetArrow(this BaseLinkModel link) { var sX = link.GetMiddleSourceX(); var sY = link.GetMiddleSourceY(); @@ -97,16 +98,16 @@ public static string CalculateAngleForTargetArrow(this LinkModel link) return angle.ToString(CultureInfo.InvariantCulture); } - public static double GetMiddleSourceX(this LinkModel link) + public static double GetMiddleSourceX(this BaseLinkModel link) => link.SourcePort.Position.X + (link.SourcePort.Size.Width / 2); - public static double GetMiddleSourceY(this LinkModel link) + public static double GetMiddleSourceY(this BaseLinkModel link) => link.SourcePort.Position.Y + (link.SourcePort.Size.Height / 2); - public static double GetMiddleTargetX(this LinkModel link) + public static double GetMiddleTargetX(this BaseLinkModel link) => link.TargetPort!.Position.X + (link.TargetPort.Size.Width / 2); - public static double GetMiddleTargetY(this LinkModel link) + public static double GetMiddleTargetY(this BaseLinkModel link) => link.TargetPort!.Position.Y + (link.TargetPort.Size.Height / 2); } } diff --git a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs new file mode 100644 index 00000000..c8234540 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs @@ -0,0 +1,54 @@ +using Blazor.Diagrams.Core.Models.Core; +using System; + +namespace Blazor.Diagrams.Core.Models.Base +{ + public abstract class BaseLinkModel : SelectableModel + { + public event Action? SourcePortChanged; + public event Action? TargetPortChanged; + + public BaseLinkModel(PortModel sourcePort, PortModel? targetPort = null) + { + SourcePort = sourcePort; + TargetPort = targetPort; + } + + public BaseLinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) : base(id) + { + SourcePort = sourcePort; + TargetPort = targetPort; + } + + public PortModel SourcePort { get; private set; } + public PortModel? TargetPort { get; private set; } + public bool IsAttached => TargetPort != null; + public Point? OnGoingPosition { get; set; } + public Router? Router { get; set; } + public PathGenerator? PathGenerator { get; set; } + public LinkMarker? SourceMarker { get; set; } + public LinkMarker? TargetMarker { get; set; } + + public void SetSourcePort(PortModel port) + { + if (SourcePort == port) + return; + + SourcePort.RemoveLink(this); + SourcePort = port; + SourcePort.AddLink(this); + SourcePortChanged?.Invoke(); + } + + public void SetTargetPort(PortModel port) + { + if (TargetPort == port) + return; + + TargetPort?.RemoveLink(this); + TargetPort = port; + TargetPort.AddLink(this); + TargetPortChanged?.Invoke(); + } + } +} diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs index 20922e0a..42543e7c 100644 --- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs +++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs @@ -1,4 +1,5 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Models.Core; using System.Collections.Generic; using System.Linq; @@ -20,7 +21,7 @@ public GroupModel(DiagramManager diagramManager, NodeModel[] children, byte padd } public NodeModel[] Children { get; private set; } - public IEnumerable HandledLinks => Children.SelectMany(c => c.AllLinks).Distinct(); + public IEnumerable HandledLinks => Children.SelectMany(c => c.AllLinks).Distinct(); public override void SetPosition(double x, double y) { diff --git a/src/Blazor.Diagrams.Core/Models/LinkModel.cs b/src/Blazor.Diagrams.Core/Models/LinkModel.cs index 5704fa0e..daf414ae 100644 --- a/src/Blazor.Diagrams.Core/Models/LinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/LinkModel.cs @@ -1,55 +1,15 @@ using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; -using System; namespace Blazor.Diagrams.Core.Models { - public class LinkModel : SelectableModel + public class LinkModel : BaseLinkModel { - public event Action? SourcePortChanged; - public event Action? TargetPortChanged; + public LinkModel(PortModel sourcePort, PortModel? targetPort = null) : base(sourcePort, targetPort) { } - public LinkModel(PortModel sourcePort, PortModel? targetPort = null) - { - SourcePort = sourcePort; - TargetPort = targetPort; - } + public LinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) : base(id, sourcePort, targetPort) { } - public LinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) : base(id) - { - SourcePort = sourcePort; - TargetPort = targetPort; - } - - public PortModel SourcePort { get; private set; } - public PortModel? TargetPort { get; private set; } - public bool IsAttached => TargetPort != null; - public Point? OnGoingPosition { get; set; } - public Router? Router { get; set; } - public PathGenerator? PathGenerator { get; set; } - public LinkMarker? SourceMarker { get; set; } - public LinkMarker? TargetMarker { get; set; } - - public void SetSourcePort(PortModel port) - { - if (SourcePort == port) - return; - - SourcePort.RemoveLink(this); - SourcePort = port; - SourcePort.AddLink(this); - SourcePortChanged?.Invoke(); - } - - public void SetTargetPort(PortModel port) - { - if (TargetPort == port) - return; - - TargetPort?.RemoveLink(this); - TargetPort = port; - TargetPort.AddLink(this); - TargetPortChanged?.Invoke(); - } + public string? Color { get; set; } + public string? SelectedColor { get; set; } + public double Width { get; set; } = 2; } } diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index 38aeb9fa..86ee55e9 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -41,7 +41,7 @@ public Size? Size public GroupModel? Group { get; internal set; } public ReadOnlyCollection Ports => _ports.AsReadOnly(); - public IEnumerable AllLinks => Ports.SelectMany(p => p.Links); + public IEnumerable AllLinks => Ports.SelectMany(p => p.Links); public PortModel AddPort(PortModel port) { diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index 8b6808e1..34853830 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -7,7 +7,7 @@ namespace Blazor.Diagrams.Core.Models { public class PortModel : Model { - private readonly List _links = new List(4); + private readonly List _links = new List(4); public PortModel(NodeModel parent, PortAlignment alignment = PortAlignment.Bottom, Point? position = null, Size? size = null) @@ -32,7 +32,7 @@ public PortModel(string id, NodeModel parent, PortAlignment alignment = PortAlig public Point Position { get; set; } public Point MiddlePosition => new Point(Position.X + Size.Width / 2, Position.Y + Size.Height / 2); public Size Size { get; set; } - public ReadOnlyCollection Links => _links.AsReadOnly(); + public ReadOnlyCollection Links => _links.AsReadOnly(); public bool Initialized { get; internal set; } public void RefreshAll() @@ -47,8 +47,8 @@ public void RefreshAll() public virtual bool CanAttachTo(PortModel port) => port != this && !port.Locked && Parent != port.Parent; - internal void AddLink(LinkModel link) => _links.Add(link); + internal void AddLink(BaseLinkModel link) => _links.Add(link); - internal void RemoveLink(LinkModel link) => _links.Remove(link); + internal void RemoveLink(BaseLinkModel link) => _links.Remove(link); } } diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs index 589361e2..ca2b599b 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -1,4 +1,5 @@ using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using System; @@ -8,7 +9,7 @@ public static partial class PathGenerators { private const double _margin = 125; - public static PathGeneratorResult Smooth(DiagramManager _, LinkModel link, Point[] route) + public static PathGeneratorResult Smooth(DiagramManager _, BaseLinkModel link, Point[] route) { if (route.Length > 2) throw new NotImplementedException(); // Curved path through points @@ -31,7 +32,7 @@ public static PathGeneratorResult Smooth(DiagramManager _, LinkModel link, Point return new PathGeneratorResult(path, sourceAngle, route[0], targetAngle, route[^1]); } - private static Point[] GetRouteWithCurvePoints(LinkModel link, Point[] route) + private static Point[] GetRouteWithCurvePoints(BaseLinkModel link, Point[] route) { var cX = (route[0].X + route[1].X) / 2; var cY = (route[0].Y + route[1].Y) / 2; diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs index addb4ad4..b82a2f6d 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using System; using System.Text; @@ -7,7 +7,7 @@ namespace Blazor.Diagrams.Core { public static partial class PathGenerators { - public static PathGeneratorResult Straight(DiagramManager _, LinkModel link, Point[] route) + public static PathGeneratorResult Straight(DiagramManager _, BaseLinkModel link, Point[] route) { route = (Point[])route.Clone(); double? sourceAngle = null; diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs index 6179751e..e782d146 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs @@ -1,11 +1,11 @@ -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; namespace Blazor.Diagrams.Core { public static partial class Routers { - public static Point[] Normal(DiagramManager _, LinkModel link, Point from, Point to) + public static Point[] Normal(DiagramManager _, BaseLinkModel link, Point from, Point to) => new[] { from, to }; } } diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index 08b2e455..43def338 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -5,11 +5,12 @@ var targetPosition = Link.IsAttached ? GetPositionBasedOnAlignment(Link.TargetPort, Link.TargetMarker) : Link.OnGoingPosition; var route = router(DiagramManager, Link, sourcePosition, targetPosition); var result = pathGenerator(DiagramManager, Link, route); - var color = Link.Selected ? DiagramManager.Options.Links.DefaultSelectedColor : DiagramManager.Options.Links.DefaultColor; + var color = Link.Selected ? (Link.SelectedColor ?? DiagramManager.Options.Links.DefaultSelectedColor) + : (Link.Color ?? DiagramManager.Options.Links.DefaultColor); } diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs index ca42decf..e68e49b4 100644 --- a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs @@ -1,5 +1,5 @@ using Blazor.Diagrams.Core; -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; using Microsoft.AspNetCore.Components.Web; @@ -15,7 +15,7 @@ public class LinkRenderer : ComponentBase, IDisposable public DiagramManager DiagramManager { get; set; } [Parameter] - public LinkModel Link { get; set; } + public BaseLinkModel Link { get; set; } public void Dispose() { @@ -59,6 +59,7 @@ private void Link_Changed() } private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Link, e); + private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Link, e); } } From 76a4bff43e42bd0023824fc75f5ab413b045e7b4 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 15 Feb 2021 13:32:20 +0100 Subject: [PATCH 11/67] feat: Link vertices (both straight and smooth generators) fix: Links are only clickable on the visible space now (pointer-events: visiblePainted) --- .../Behaviors/DragMovablesBehavior.cs | 2 +- src/Blazor.Diagrams.Core/DiagramManager.cs | 28 ++- .../Geometry/BezierSpline.cs | 207 ++++++++++++++++++ .../Models/Base/BaseLinkModel.cs | 3 + .../Models/LinkVertexModel.cs | 22 ++ .../PathGenerators/PathGeneratorResult.cs | 6 +- .../PathGenerators/PathGenerators.Smooth.cs | 35 ++- .../PathGenerators/PathGenerators.Straight.cs | 10 +- .../Routers/Routers.Normal.cs | 13 +- .../Components/LinkVertexWidget.razor | 9 + .../Components/LinkVertexWidget.razor.cs | 51 +++++ .../Components/LinkWidget.razor | 42 +++- .../Components/LinkWidget.razor.cs | 14 +- .../Components/Renderers/NodeRenderer.cs | 5 +- src/Blazor.Diagrams/wwwroot/style.css | 12 +- 15 files changed, 427 insertions(+), 32 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs create mode 100644 src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs create mode 100644 src/Blazor.Diagrams/Components/LinkVertexWidget.razor create mode 100644 src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index bf034d0f..da35a5d5 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -24,7 +24,7 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) if (!(model is MovableModel)) return; - // Don't link this linq + // Don't like this linq _initialPositions = DiagramManager.GetSelectedModels() .Where(m => m is MovableModel) .Select(m => (m as MovableModel)!.Position) diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 3994f480..db5d007f 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -3,6 +3,7 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Web; using System; using System.Collections.Generic; @@ -189,10 +190,29 @@ public void RemoveGroup(GroupModel group) public IEnumerable GetSelectedModels() { - var selectedNodes = Nodes.Where(n => n.Selected); - var selectedLinks = Links.Where(n => n.Selected).Cast(); - var selectedGroups = Groups.Where(n => n.Selected); - return selectedNodes.Union(selectedLinks.Union(selectedGroups)); + foreach (var node in Nodes) + { + if (node.Selected) + yield return node; + } + + foreach (var link in Links) + { + if (link.Selected) + yield return link; + + foreach (var vertex in link.Vertices) + { + if (vertex.Selected) + yield return vertex; + } + } + + foreach (var group in Groups) + { + if (group.Selected) + yield return group; + } } public void SelectModel(SelectableModel model, bool unselectOthers) diff --git a/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs b/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs new file mode 100644 index 00000000..071bfa32 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs @@ -0,0 +1,207 @@ +using Blazor.Diagrams.Core.Models.Core; +using System; + +namespace Blazor.Diagrams.Core.Geometry +{ + /// + /// Bezier Spline methods + /// + /// + /// Modified: Peter Lee (peterlee.com.cn < at > gmail.com) + /// Update: 2009-03-16 + /// + /// see also: + /// Draw a smooth curve through a set of 2D points with Bezier primitives + /// http://www.codeproject.com/KB/graphics/BezierSpline.aspx + /// By Oleg V. Polikarpotchkin + /// + /// Algorithm Descripition: + /// + /// To make a sequence of individual Bezier curves to be a spline, we + /// should calculate Bezier control points so that the spline curve + /// has two continuous derivatives at knot points. + /// + /// Note: `[]` denotes subscript + /// `^` denotes supscript + /// `'` denotes first derivative + /// `''` denotes second derivative + /// + /// A Bezier curve on a single interval can be expressed as: + /// + /// B(t) = (1-t)^3 P0 + 3(1-t)^2 t P1 + 3(1-t)t^2 P2 + t^3 P3 (*) + /// + /// where t is in [0,1], and + /// 1. P0 - first knot point + /// 2. P1 - first control point (close to P0) + /// 3. P2 - second control point (close to P3) + /// 4. P3 - second knot point + /// + /// The first derivative of (*) is: + /// + /// B'(t) = -3(1-t)^2 P0 + 3(3t^2–4t+1) P1 + 3(2–3t)t P2 + 3t^2 P3 + /// + /// The second derivative of (*) is: + /// + /// B''(t) = 6(1-t) P0 + 6(3t-2) P1 + 6(1–3t) P2 + 6t P3 + /// + /// Considering a set of piecewise Bezier curves with n+1 points + /// (Q[0..n]) and n subintervals, the (i-1)-th curve should connect + /// to the i-th one: + /// + /// Q[0] = P0[1], + /// Q[1] = P0[2] = P3[1], ... , Q[i-1] = P0[i] = P3[i-1] (i = 1..n) (@) + /// + /// At the i-th subinterval, the Bezier curve is: + /// + /// B[i](t) = (1-t)^3 P0[i] + 3(1-t)^2 t P1[i] + + /// 3(1-t)t^2 P2[i] + t^3 P3[i] (i = 1..n) + /// + /// applying (@): + /// + /// B[i](t) = (1-t)^3 Q[i-1] + 3(1-t)^2 t P1[i] + + /// 3(1-t)t^2 P2[i] + t^3 Q[i] (i = 1..n) (i) + /// + /// From (i), the first derivative at the i-th subinterval is: + /// + /// B'[i](t) = -3(1-t)^2 Q[i-1] + 3(3t^2–4t+1) P1[i] + + /// 3(2–3t)t P2[i] + 3t^2 Q[i] (i = 1..n) + /// + /// Using the first derivative continuity condition: + /// + /// B'[i-1](1) = B'[i](0) + /// + /// we get: + /// + /// P1[i] + P2[i-1] = 2Q[i-1] (i = 2..n) (1) + /// + /// From (i), the second derivative at the i-th subinterval is: + /// + /// B''[i](t) = 6(1-t) Q[i-1] + 6(3t-2) P1[i] + + /// 6(1-3t) P2[i] + 6t Q[i] (i = 1..n) + /// + /// Using the second derivative continuity condition: + /// + /// B''[i-1](1) = B''[i](0) + /// + /// we get: + /// + /// P1[i-1] + 2P1[i] = P2[i] + 2P2[i-1] (i = 2..n) (2) + /// + /// Then, using the so-called "natural conditions": + /// + /// B''[1](0) = 0 + /// + /// B''[n](1) = 0 + /// + /// to the second derivative equations, and we get: + /// + /// 2P1[1] - P2[1] = Q[0] (3) + /// + /// 2P2[n] - P1[n] = Q[n] (4) + /// + /// From (1)(2)(3)(4), we have 2n conditions for n first control points + /// P1[1..n], and n second control points P2[1..n]. + /// + /// Eliminating P2[1..n], we get (be patient to get :-) a set of n + /// equations for solving P1[1..n]: + /// + /// 2P1[1] + P1[2] + = Q[0] + 2Q[1] + /// P1[1] + 4P1[2] + P1[3] = 4Q[1] + 2Q[2] + /// ... + /// P1[i-1] + 4P1[i] + P1[i+1] = 4Q[i-1] + 2Q[i] + /// ... + /// P1[n-2] + 4P1[n-1] + P1[n] = 4Q[n-2] + 2Q[n-1] + /// P1[n-1] + 3.5P1[n] = (8Q[n-1] + Q[n]) / 2 + /// + /// From this set of equations, P1[1..n] are easy but tedious to solve. + /// + public static class BezierSpline + { + /// + /// Get open-ended Bezier Spline Control Points. + /// + /// Input Knot Bezier spline points. + /// Output First Control points array of knots.Length - 1 length. + /// Output Second Control points array of knots.Length - 1 length. + /// parameter must be not null. + /// array must containg at least two points. + public static void GetCurveControlPoints(Point[] knots, out Point[] firstControlPoints, out Point[] secondControlPoints) + { + if (knots == null) + throw new ArgumentNullException("knots"); + int n = knots.Length - 1; + if (n < 1) + throw new ArgumentException("At least two knot points required", "knots"); + if (n == 1) + { // Special case: Bezier curve should be a straight line. + firstControlPoints = new Point[1]; + // 3P1 = 2P0 + P3 + firstControlPoints[0] = new Point((2 * knots[0].X + knots[1].X) / 3, (2 * knots[0].Y + knots[1].Y) / 3); + + secondControlPoints = new Point[1]; + // P2 = 2P1 – P0 + secondControlPoints[0] = new Point(2 * firstControlPoints[0].X - knots[0].X, 2 * firstControlPoints[0].Y - knots[0].Y); + return; + } + + // Calculate first Bezier control points + // Right hand side vector + double[] rhs = new double[n]; + + // Set right hand side X values + for (int i = 1; i < n - 1; ++i) + rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X; + rhs[0] = knots[0].X + 2 * knots[1].X; + rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0; + // Get first control points X-values + double[] x = GetFirstControlPoints(rhs); + + // Set right hand side Y values + for (int i = 1; i < n - 1; ++i) + rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y; + rhs[0] = knots[0].Y + 2 * knots[1].Y; + rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0; + // Get first control points Y-values + double[] y = GetFirstControlPoints(rhs); + + // Fill output arrays. + firstControlPoints = new Point[n]; + secondControlPoints = new Point[n]; + for (int i = 0; i < n; ++i) + { + // First control point + firstControlPoints[i] = new Point(x[i], y[i]); + // Second control point + if (i < n - 1) + secondControlPoints[i] = new Point(2 * knots[i + 1].X - x[i + 1], 2 * knots[i + 1].Y - y[i + 1]); + else + secondControlPoints[i] = new Point((knots[n].X + x[n - 1]) / 2, (knots[n].Y + y[n - 1]) / 2); + } + } + + /// + /// Solves a tridiagonal system for one of coordinates (x or y) of first Bezier control points. + /// + /// Right hand side vector. + /// Solution vector. + private static double[] GetFirstControlPoints(double[] rhs) + { + int n = rhs.Length; + double[] x = new double[n]; // Solution vector. + double[] tmp = new double[n]; // Temp workspace. + + double b = 2.0; + x[0] = rhs[0] / b; + for (int i = 1; i < n; i++) // Decomposition and forward substitution. + { + tmp[i] = 1 / b; + b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; + x[i] = (rhs[i] - x[i - 1]) / b; + } + for (int i = 1; i < n; i++) + x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. + + return x; + } + } +} diff --git a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs index c8234540..443b6eea 100644 --- a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs @@ -1,5 +1,6 @@ using Blazor.Diagrams.Core.Models.Core; using System; +using System.Collections.Generic; namespace Blazor.Diagrams.Core.Models.Base { @@ -28,6 +29,8 @@ public BaseLinkModel(string id, PortModel sourcePort, PortModel? targetPort = nu public PathGenerator? PathGenerator { get; set; } public LinkMarker? SourceMarker { get; set; } public LinkMarker? TargetMarker { get; set; } + public bool Segmentable { get; set; } = false; + public List Vertices { get; } = new List(); public void SetSourcePort(PortModel port) { diff --git a/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs b/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs new file mode 100644 index 00000000..efa12767 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs @@ -0,0 +1,22 @@ +using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Core.Models +{ + public class LinkVertexModel : MovableModel + { + public LinkVertexModel(BaseLinkModel parent, Point? position = null) : base(position) + { + Parent = parent; + } + + public BaseLinkModel Parent { get; } + + public override void SetPosition(double x, double y) + { + base.SetPosition(x, y); + Refresh(); + Parent.Refresh(); + } + } +} diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs index ebe4b0ee..018893be 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs @@ -4,17 +4,17 @@ namespace Blazor.Diagrams.Core { public class PathGeneratorResult { - public PathGeneratorResult(string path, double? sourceMarkerAngle = null, Point? sourceMarkerPosition = null, + public PathGeneratorResult(string[] paths, double? sourceMarkerAngle = null, Point? sourceMarkerPosition = null, double? targetMarkerAngle = null, Point? targetMarkerPosition = null) { - Path = path; + Paths = paths; SourceMarkerAngle = sourceMarkerAngle; SourceMarkerPosition = sourceMarkerPosition; TargetMarkerAngle = targetMarkerAngle; TargetMarkerPosition = targetMarkerPosition; } - public string Path { get; } + public string[] Paths { get; } public double? SourceMarkerAngle { get; } public Point? SourceMarkerPosition { get; } public double? TargetMarkerAngle { get; } diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs index ca2b599b..c10d762f 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -1,4 +1,5 @@ -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using System; @@ -12,7 +13,7 @@ public static partial class PathGenerators public static PathGeneratorResult Smooth(DiagramManager _, BaseLinkModel link, Point[] route) { if (route.Length > 2) - throw new NotImplementedException(); // Curved path through points + return CurveThroughPoints(route, link); route = GetRouteWithCurvePoints(link, route); double? sourceAngle = null; @@ -29,7 +30,35 @@ public static PathGeneratorResult Smooth(DiagramManager _, BaseLinkModel link, P } var path = FormattableString.Invariant($"M {route[0].X} {route[0].Y} C {route[1].X} {route[1].Y}, {route[2].X} {route[2].Y}, {route[3].X} {route[3].Y}"); - return new PathGeneratorResult(path, sourceAngle, route[0], targetAngle, route[^1]); + return new PathGeneratorResult(new[] { path }, sourceAngle, route[0], targetAngle, route[^1]); + } + + private static PathGeneratorResult CurveThroughPoints(Point[] route, BaseLinkModel link) + { + double? sourceAngle = null; + double? targetAngle = null; + + if (link.SourceMarker != null) + { + sourceAngle = SourceMarkerAdjustement(route, link.SourceMarker.Width); + } + + if (link.TargetMarker != null) + { + targetAngle = TargetMarkerAdjustement(route, link.TargetMarker.Width); + } + + BezierSpline.GetCurveControlPoints(route, out var firstControlPoints, out var secondControlPoints); + var paths = new string[firstControlPoints.Length]; + + for (var i = 0; i < firstControlPoints.Length; i++) + { + var cp1 = firstControlPoints[i]; + var cp2 = secondControlPoints[i]; + paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} C {cp1.X} {cp1.Y}, {cp2.X} {cp2.Y}, {route[i + 1].X} {route[i + 1].Y}"); + } + + return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[^1]); } private static Point[] GetRouteWithCurvePoints(BaseLinkModel link, Point[] route) diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs index b82a2f6d..6f03843d 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs @@ -1,7 +1,6 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using System; -using System.Text; namespace Blazor.Diagrams.Core { @@ -23,14 +22,13 @@ public static PathGeneratorResult Straight(DiagramManager _, BaseLinkModel link, targetAngle = TargetMarkerAdjustement(route, link.TargetMarker.Width); } - var sb = new StringBuilder(FormattableString.Invariant($"M {route[0].X} {route[0].Y}")); - - for (var i = 1; i < route.Length; i++) + var paths = new string[route.Length - 1]; + for (var i = 0; i < route.Length - 1; i++) { - sb.Append(FormattableString.Invariant($" L {route[i].X} {route[i].Y}")); + paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} L {route[i + 1].X} {route[i + 1].Y}"); } - return new PathGeneratorResult(sb.ToString(), sourceAngle, route[0], targetAngle, route[^1]); + return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[^1]); } } } diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs index e782d146..f9353dcf 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs @@ -1,11 +1,22 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; +using System; +using System.Linq; namespace Blazor.Diagrams.Core { public static partial class Routers { public static Point[] Normal(DiagramManager _, BaseLinkModel link, Point from, Point to) - => new[] { from, to }; + { + var route = new Point[link.Vertices.Count + 2]; + route[0] = from; + if (link.Vertices.Count > 0) + { + Array.Copy(link.Vertices.Select(v => v.Position).ToArray(), 0, route, 1, link.Vertices.Count); + } + route[^1] = to; + return route; + } } } diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor new file mode 100644 index 00000000..eea78dc3 --- /dev/null +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs new file mode 100644 index 00000000..445970a4 --- /dev/null +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs @@ -0,0 +1,51 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Models; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using System; + +namespace Blazor.Diagrams.Components +{ + public partial class LinkVertexWidget : IDisposable + { + private bool _shouldRender = true; + + [CascadingParameter(Name = "DiagramManager")] public DiagramManager DiagramManager { get; set; } + [Parameter] public LinkVertexModel Vertex { get; set; } + [Parameter] public string Color { get; set; } + [Parameter] public string SelectedColor { get; set; } + + private string ColorToUse => Vertex.Selected ? SelectedColor : Color; + + public void Dispose() + { + Vertex.Changed -= OnVertexChanged; + } + + protected override void OnInitialized() + { + Vertex.Changed += OnVertexChanged; + } + + protected override bool ShouldRender() + { + if (_shouldRender) + { + _shouldRender = false; + return true; + } + + return false; + } + + private void OnVertexChanged() + { + _shouldRender = true; + StateHasChanged(); + } + + private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Vertex, e); + + private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Vertex, e); + } +} diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index 43def338..54be0d21 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -9,10 +9,27 @@ : (Link.Color ?? DiagramManager.Options.Links.DefaultColor); } - +@for (var i = 0; i < result.Paths.Length; i++) +{ + var index = i; + + + @if (Link.IsAttached) + { + + } +} @if (Link.SourceMarker != null && result.SourceMarkerAngle != null && result.SourceMarkerPosition != null) { @@ -26,4 +43,19 @@ -} \ No newline at end of file +} + +@if (Link.Vertices.Count > 0) +{ + var selectedColor = Link.SelectedColor ?? DiagramManager.Options.Links.DefaultSelectedColor; + var normalColor = Link.Color ?? DiagramManager.Options.Links.DefaultColor; + + @foreach (var vertex in Link.Vertices) + { + + } + +} \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index 19c4a46d..6f73ce02 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -2,12 +2,13 @@ using Blazor.Diagrams.Core; using Microsoft.AspNetCore.Components; using Blazor.Diagrams.Core.Models.Core; +using Microsoft.AspNetCore.Components.Web; +using System; namespace Blazor.Diagrams.Components { public partial class LinkWidget { - [CascadingParameter(Name = "DiagramManager")] public DiagramManager DiagramManager { get; set; } @@ -40,5 +41,16 @@ private Point GetPositionBasedOnAlignment(PortModel port, LinkMarker marker) return pt; } } + + private void OnMouseDown(MouseEventArgs e, int index) + { + if (!Link.Segmentable) + return; + + var rPt = DiagramManager.GetRelativePoint(e.ClientX, e.ClientY); + var vertex = new LinkVertexModel(Link, rPt); + Link.Vertices.Insert(index, vertex); + DiagramManager.OnMouseDown(vertex, e); + } } } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index 46dcf033..06351649 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -36,13 +36,10 @@ public void Dispose() DiagramManager.ContainerChanged -= CheckVisibility; Node.Changed -= ReRender; - if (_reference == null) - return; - if (_element.Id != null) _ = JsRuntime.UnobserveResizes(_element); - _reference.Dispose(); + _reference?.Dispose(); } [JSInvokable] diff --git a/src/Blazor.Diagrams/wwwroot/style.css b/src/Blazor.Diagrams/wwwroot/style.css index c7a23f1b..020795b2 100644 --- a/src/Blazor.Diagrams/wwwroot/style.css +++ b/src/Blazor.Diagrams/wwwroot/style.css @@ -41,13 +41,17 @@ } .link { - pointer-events: all; + pointer-events: visiblePainted; cursor: pointer; } -.diagram-navigator { - z-index: 10; -} + .link path.selection-helper:hover { + stroke-opacity: 0.05; + } + + .diagram-navigator { + z-index: 10; + } .diagram-navigator .current-view { position: absolute; From 023a6e0e09995e76222d334cb54cbe4ae30506d4 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 15 Feb 2021 15:02:34 +0100 Subject: [PATCH 12/67] feat: Remove LinkVertex on double click --- .../PathGenerators/PathGenerators.Smooth.cs | 1 + src/Blazor.Diagrams/Components/LinkVertexWidget.razor | 1 + src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs index c10d762f..1cca62c0 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -58,6 +58,7 @@ private static PathGeneratorResult CurveThroughPoints(Point[] route, BaseLinkMod paths[i] = FormattableString.Invariant($"M {route[i].X} {route[i].Y} C {cp1.X} {cp1.Y}, {cp2.X} {cp2.Y}, {route[i + 1].X} {route[i + 1].Y}"); } + // Todo: adjust marker positions based on closest control points return new PathGeneratorResult(paths, sourceAngle, route[0], targetAngle, route[^1]); } diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor index eea78dc3..762f909d 100644 --- a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor @@ -3,6 +3,7 @@ r="5" fill="@ColorToUse" cursor="move" + @ondblclick="OnDoubleClick" @onmousedown="OnMouseDown" @onmousedown:stopPropagation @onmouseup="OnMouseUp" diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs index 445970a4..4c4467ff 100644 --- a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs @@ -47,5 +47,11 @@ private void OnVertexChanged() private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Vertex, e); private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Vertex, e); + + private void OnDoubleClick(MouseEventArgs e) + { + Vertex.Parent.Vertices.Remove(Vertex); + Vertex.Parent.Refresh(); + } } } From 08e9429fba32e3c468f06cecdc817ec795e2fe02 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 19 Feb 2021 12:54:19 +0100 Subject: [PATCH 13/67] feat: Link labels --- .../Models/Base/BaseLinkModel.cs | 1 + .../Models/LinkLabelModel.cs | 35 +++++++++ src/Blazor.Diagrams/Blazor.Diagrams.csproj | 1 + .../Components/DefaultLinkLabelWidget.razor | 9 +++ .../Components/LinkWidget.razor | 11 ++- .../Components/Renderers/LinkLabelRenderer.cs | 75 +++++++++++++++++++ .../wwwroot/default.styles.css | 15 +++- src/Blazor.Diagrams/wwwroot/style.css | 17 +++-- 8 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs create mode 100644 src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor create mode 100644 src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs diff --git a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs index 443b6eea..d7f6b30b 100644 --- a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs @@ -31,6 +31,7 @@ public BaseLinkModel(string id, PortModel sourcePort, PortModel? targetPort = nu public LinkMarker? TargetMarker { get; set; } public bool Segmentable { get; set; } = false; public List Vertices { get; } = new List(); + public List Labels { get; set; } = new List(); public void SetSourcePort(PortModel port) { diff --git a/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs b/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs new file mode 100644 index 00000000..5d8d366d --- /dev/null +++ b/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs @@ -0,0 +1,35 @@ +using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Core.Models +{ + public class LinkLabelModel : Model + { + public LinkLabelModel(BaseLinkModel parent, string id, string content, double? distance = null, Point? offset = null) : base(id) + { + Parent = parent; + Content = content; + Distance = distance; + Offset = offset; + } + + public LinkLabelModel(BaseLinkModel parent, string content, double? distance = null, Point? offset = null) + { + Parent = parent; + Content = content; + Distance = distance; + Offset = offset; + } + + public BaseLinkModel Parent { get; } + public string Content { get; set; } + /// + /// 3 types of values are possible: + /// - A number between 0 and 1: Position relative to the link's length + /// - A positive number, greater than 1: Position away from the start + /// - A negative number, less than 0: Position away from the end + /// + public double? Distance { get; set; } + public Point? Offset { get; set; } + } +} diff --git a/src/Blazor.Diagrams/Blazor.Diagrams.csproj b/src/Blazor.Diagrams/Blazor.Diagrams.csproj index 526e97f3..523cbe8d 100644 --- a/src/Blazor.Diagrams/Blazor.Diagrams.csproj +++ b/src/Blazor.Diagrams/Blazor.Diagrams.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor b/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor new file mode 100644 index 00000000..00ac819c --- /dev/null +++ b/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor @@ -0,0 +1,9 @@ +@((MarkupString)$"") + +@code { + [Parameter] public LinkLabelModel Label { get; set; } + [Parameter] public Point Position { get; set; } + + private double X => Position.X - (Label.Offset?.X ?? 0); + private double Y => Position.Y - (Label.Offset?.Y ?? 0); +} diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index 54be0d21..f298d850 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -1,4 +1,5 @@ -@{ +@using SvgPathProperties; +@{ var router = Link.Router ?? DiagramManager.Options.Links.DefaultRouter; var pathGenerator = Link.PathGenerator ?? DiagramManager.Options.Links.DefaultPathGenerator; var sourcePosition = GetPositionBasedOnAlignment(Link.SourcePort, Link.SourceMarker); @@ -7,6 +8,7 @@ var result = pathGenerator(DiagramManager, Link, route); var color = Link.Selected ? (Link.SelectedColor ?? DiagramManager.Options.Links.DefaultSelectedColor) : (Link.Color ?? DiagramManager.Options.Links.DefaultColor); + var paths = Link.Labels.Count > 0 ? result.Paths.Select(p => new SVGPathProperties(p)).ToArray() : Array.Empty(); } @for (var i = 0; i < result.Paths.Length; i++) @@ -58,4 +60,9 @@ SelectedColor="@selectedColor" /> } -} \ No newline at end of file +} + +@foreach (var label in Link.Labels) +{ + +} \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs new file mode 100644 index 00000000..8f64e5b4 --- /dev/null +++ b/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs @@ -0,0 +1,75 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; +using SvgPathProperties; +using System; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Blazor.Diagrams.Components.Renderers +{ + public class LinkLabelRenderer : ComponentBase, IDisposable + { + [CascadingParameter(Name = "DiagramManager")] public DiagramManager DiagramManager { get; set; } + [Parameter] public LinkLabelModel Label { get; set; } + [Parameter] public SVGPathProperties[] Paths { get; set; } + + public void Dispose() + { + Label.Changed -= OnLabelChanged; + } + + protected override void OnInitialized() + { + Label.Changed += OnLabelChanged; + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + var component = DiagramManager.GetComponentForModel(Label) ?? typeof(DefaultLinkLabelWidget); + var position = FindPosition(); + if (position == null) + return; + + builder.OpenElement(0, "g"); + builder.OpenComponent(1, component); + builder.AddAttribute(2, "Label", Label); + builder.AddAttribute(3, "Position", position); + builder.CloseComponent(); + builder.CloseElement(); // g + } + + private void OnLabelChanged() => StateHasChanged(); + + private Point FindPosition() + { + var totalLength = Paths.Sum(p => p.GetTotalLength()); + + var length = Label.Distance switch + { + var d when d >= 0 && d <= 1 => Label.Distance.Value * totalLength, + var d when d > 1 => Label.Distance.Value, + var d when d < 0 => totalLength + Label.Distance.Value, + _ => totalLength * (Label.Parent.Labels.IndexOf(Label) + 1) / (Label.Parent.Labels.Count + 1) + }; + + foreach (var path in Paths) + { + var pathLength = path.GetTotalLength(); + if (length < pathLength) + { + var pt = path.GetPointAtLength(length); + return new Point(pt.X, pt.Y); + } + + length -= pathLength; + } + + return null; + } + } +} diff --git a/src/Blazor.Diagrams/wwwroot/default.styles.css b/src/Blazor.Diagrams/wwwroot/default.styles.css index df471b9e..dc1135fd 100644 --- a/src/Blazor.Diagrams/wwwroot/default.styles.css +++ b/src/Blazor.Diagrams/wwwroot/default.styles.css @@ -95,4 +95,17 @@ .group.default.selected { outline: 2px solid #6e9fd4; - } \ No newline at end of file + } + +.link .link-label { + display: inline-block; + color: #fff; + background-color: rgb(110, 159, 212); + border-radius: 0.25rem; + padding: 0.25rem; + text-align: center; + font-size: 0.875rem; + user-select: none; + min-width: 3rem; + transform: translate(-50%, -50%); +} \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/style.css b/src/Blazor.Diagrams/wwwroot/style.css index 020795b2..3fa888d6 100644 --- a/src/Blazor.Diagrams/wwwroot/style.css +++ b/src/Blazor.Diagrams/wwwroot/style.css @@ -36,9 +36,9 @@ pointer-events: all; } -.node.locked { - cursor: pointer; -} + .node.locked { + cursor: pointer; + } .link { pointer-events: visiblePainted; @@ -49,9 +49,9 @@ stroke-opacity: 0.05; } - .diagram-navigator { - z-index: 10; - } +.diagram-navigator { + z-index: 10; +} .diagram-navigator .current-view { position: absolute; @@ -70,3 +70,8 @@ overflow: visible; pointer-events: none; } + +.link foreignObject { + overflow: visible; + pointer-events: none; +} \ No newline at end of file From 0a1cf27af6f5b38c7a18cdf1b577cca818f531e4 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 19 Feb 2021 12:56:48 +0100 Subject: [PATCH 14/67] fix: default link label offset calculation --- src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor b/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor index 00ac819c..292d3dc0 100644 --- a/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor +++ b/src/Blazor.Diagrams/Components/DefaultLinkLabelWidget.razor @@ -4,6 +4,6 @@ [Parameter] public LinkLabelModel Label { get; set; } [Parameter] public Point Position { get; set; } - private double X => Position.X - (Label.Offset?.X ?? 0); - private double Y => Position.Y - (Label.Offset?.Y ?? 0); + private double X => Position.X + (Label.Offset?.X ?? 0); + private double Y => Position.Y + (Label.Offset?.Y ?? 0); } From 565b86a5661980e2a1a51372c1049c6550b2436f Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 19 Feb 2021 15:17:37 +0100 Subject: [PATCH 15/67] feat: Link snapping --- .../Behaviors/DragNewLinkBehavior.cs | 34 +++++++++++++++++++ src/Blazor.Diagrams.Core/DiagramManager.cs | 1 - src/Blazor.Diagrams.Core/DiagramOptions.cs | 4 +++ .../Models/Base/BaseLinkModel.cs | 4 +-- src/Blazor.Diagrams.Core/Models/Core/Point.cs | 7 +++- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index fd506dc5..cab4ffb3 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -2,6 +2,7 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; +using System.Linq; namespace Blazor.Diagrams.Core.Behaviors { @@ -29,6 +30,7 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) _ongoingLink = new LinkModel(port, null); _ongoingLink.OnGoingPosition = new Point(port.Position.X + port.Size.Width / 2, port.Position.Y + port.Size.Height / 2); + _ongoingLink.TargetMarker = LinkMarker.Arrow; DiagramManager.Links.Add(_ongoingLink); } @@ -41,7 +43,21 @@ private void DiagramManager_MouseMove(Model model, MouseEventArgs e) var deltaY = (e.ClientY - _initialY) / DiagramManager.Zoom; var sX = _ongoingLink.SourcePort.Position.X + _ongoingLink.SourcePort.Size.Width / 2; var sY = _ongoingLink.SourcePort.Position.Y + _ongoingLink.SourcePort.Size.Height / 2; + _ongoingLink.OnGoingPosition = new Point(sX + deltaX, sY + deltaY); + + if (DiagramManager.Options.Links.EnableSnapping) + { + var nearPort = FindNearPortToAttachTo(); + if (nearPort != null || _ongoingLink.TargetPort != null) + { + var oldPort = _ongoingLink.TargetPort; + _ongoingLink.SetTargetPort(nearPort); + oldPort?.Refresh(); + nearPort?.Refresh(); + } + } + _ongoingLink.Refresh(); } @@ -50,6 +66,12 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) if (_ongoingLink == null) return; + if (_ongoingLink.IsAttached) // Snapped already + { + _ongoingLink = null; + return; + } + if (!(model is PortModel port) || !_ongoingLink.SourcePort.CanAttachTo(port)) { DiagramManager.Links.Remove(_ongoingLink); @@ -65,6 +87,18 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) _ongoingLink = null; } + private PortModel? FindNearPortToAttachTo() + { + foreach (var port in DiagramManager.Nodes.SelectMany(n => n.Ports)) + { + if (_ongoingLink!.OnGoingPosition!.DistanceTo(port.Position) < DiagramManager.Options.Links.SnappingRadius && + _ongoingLink.SourcePort.CanAttachTo(port)) + return port; + } + + return null; + } + public override void Dispose() { DiagramManager.MouseDown -= DiagramManager_MouseDown; diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index db5d007f..58adb426 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -3,7 +3,6 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Web; using System; using System.Collections.Generic; diff --git a/src/Blazor.Diagrams.Core/DiagramOptions.cs b/src/Blazor.Diagrams.Core/DiagramOptions.cs index 1d05ae7e..1c450981 100644 --- a/src/Blazor.Diagrams.Core/DiagramOptions.cs +++ b/src/Blazor.Diagrams.Core/DiagramOptions.cs @@ -36,6 +36,10 @@ public class DiagramLinkOptions public Router DefaultRouter { get; set; } = Routers.Normal; [Description("Default PathGenerator for links")] public PathGenerator DefaultPathGenerator { get; set; } = PathGenerators.Smooth; + [Description("Whether to enable link snapping")] + public bool EnableSnapping { get; set; } + [Description("Link snapping radius")] + public double SnappingRadius { get; set; } = 50; } public class DiagramZoomOptions diff --git a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs index d7f6b30b..340b7c10 100644 --- a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs @@ -44,14 +44,14 @@ public void SetSourcePort(PortModel port) SourcePortChanged?.Invoke(); } - public void SetTargetPort(PortModel port) + public void SetTargetPort(PortModel? port) { if (TargetPort == port) return; TargetPort?.RemoveLink(this); TargetPort = port; - TargetPort.AddLink(this); + TargetPort?.AddLink(this); TargetPortChanged?.Invoke(); } } diff --git a/src/Blazor.Diagrams.Core/Models/Core/Point.cs b/src/Blazor.Diagrams.Core/Models/Core/Point.cs index a32d432c..64c099a1 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Point.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Point.cs @@ -1,4 +1,6 @@ -namespace Blazor.Diagrams.Core.Models.Core +using System; + +namespace Blazor.Diagrams.Core.Models.Core { public class Point { @@ -20,6 +22,9 @@ public Point(double x, double y) public Point Substract(double value) => new Point(X - value, Y - value); public Point Substract(double x, double y) => new Point(X - x, Y - y); + public double DistanceTo(Point other) + => Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2)); + public override string ToString() => $"Point(x={X}, y={Y})"; public static Point operator -(Point a, Point b) => new Point(a.X - b.X, a.Y - b.Y); From e65bffbc0db7a3e0f175abd13c830e94694a0b23 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 19 Feb 2021 16:26:47 +0100 Subject: [PATCH 16/67] fix: MouseUp event bubbles up from PortModel to NodeModel --- src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor index 9563eed5..9d946ecc 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor @@ -3,6 +3,7 @@ @ref="_element" @onmousedown="OnMouseDown" @onmousedown:stopPropagation - @onmouseup="OnMouseUp"> + @onmouseup="OnMouseUp" + @onmouseup:stopPropagation> @ChildContent \ No newline at end of file From e9e5f20a53668928d47f7bb0693ea531de7385cc Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 20 Feb 2021 01:12:28 +0100 Subject: [PATCH 17/67] feat: Add old ports to SourcePortChanged/TargetPortChanged events --- .../Models/Base/BaseLinkModel.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs index 340b7c10..d1becbdb 100644 --- a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs @@ -6,8 +6,16 @@ namespace Blazor.Diagrams.Core.Models.Base { public abstract class BaseLinkModel : SelectableModel { - public event Action? SourcePortChanged; - public event Action? TargetPortChanged; + /// + /// An event that fires when the SourcePort changes. + /// The PortModel instance in the parameters is the old value. + /// + public event Action? SourcePortChanged; + /// + /// An event that fires when the TargetPort changes. + /// The PortModel instance in the parameters is the old value. + /// + public event Action? TargetPortChanged; public BaseLinkModel(PortModel sourcePort, PortModel? targetPort = null) { @@ -38,10 +46,11 @@ public void SetSourcePort(PortModel port) if (SourcePort == port) return; + var old = SourcePort; SourcePort.RemoveLink(this); SourcePort = port; SourcePort.AddLink(this); - SourcePortChanged?.Invoke(); + SourcePortChanged?.Invoke(old); } public void SetTargetPort(PortModel? port) @@ -49,10 +58,11 @@ public void SetTargetPort(PortModel? port) if (TargetPort == port) return; + var old = TargetPort; TargetPort?.RemoveLink(this); TargetPort = port; TargetPort?.AddLink(this); - TargetPortChanged?.Invoke(); + TargetPortChanged?.Invoke(old); } } } From e63ae0e1f646801410b0b729795a1761e0d2aca6 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 20 Feb 2021 20:53:10 +0100 Subject: [PATCH 18/67] refactor: GetNodesRect into GetBounds extension method --- .../Demos/CustomGroup/CustomGroupModel.cs | 7 ++-- .../Demos/CustomGroup/Demo.razor.cs | 2 +- src/Blazor.Diagrams.Core/DiagramManager.cs | 39 ++---------------- .../Extensions/DiagramExtensions.cs | 41 +++++++++++++++++++ src/Blazor.Diagrams.Core/Models/GroupModel.cs | 20 ++++----- .../Components/Navigator.razor.cs | 3 +- 6 files changed, 60 insertions(+), 52 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs diff --git a/samples/SharedDemo/Demos/CustomGroup/CustomGroupModel.cs b/samples/SharedDemo/Demos/CustomGroup/CustomGroupModel.cs index c09fdb0b..021431e8 100644 --- a/samples/SharedDemo/Demos/CustomGroup/CustomGroupModel.cs +++ b/samples/SharedDemo/Demos/CustomGroup/CustomGroupModel.cs @@ -1,12 +1,11 @@ -using Blazor.Diagrams.Core; -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models; namespace SharedDemo.Demos.CustomGroup { public class CustomGroupModel : GroupModel { - public CustomGroupModel(DiagramManager diagramManager, NodeModel[] children, string title, byte padding = 30) - : base(diagramManager, children, padding) + public CustomGroupModel(NodeModel[] children, string title, byte padding = 30) + : base(children, padding) { Title = title; } diff --git a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs index f152198d..2c82e19a 100644 --- a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs @@ -23,7 +23,7 @@ protected override void OnInitialized() var node3 = NewNode(500, 100); _diagramManager.Nodes.Add(node1, node2, node3); - _diagramManager.AddGroup(new CustomGroupModel(_diagramManager, new[] { node2, node3 }, "Group 1")); + _diagramManager.AddGroup(new CustomGroupModel(new[] { node2, node3 }, "Group 1")); _diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); _diagramManager.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 58adb426..05a44af3 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -1,4 +1,5 @@ using Blazor.Diagrams.Core.Behaviors; +using Blazor.Diagrams.Core.Extensions; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; @@ -126,7 +127,7 @@ public GroupModel Group(params NodeModel[] children) if (children.Any(n => n.Group != null)) throw new InvalidOperationException("Cannot group nodes that already belong to another group"); - var group = new GroupModel(this, children); + var group = new GroupModel(children); AddGroup(group); return group; } @@ -298,7 +299,8 @@ public void ZoomToFit(double margin = 10) return; var selectedNodes = Nodes.Where(s => s.Selected); - (var minX, var maxX, var minY, var maxY) = GetNodesRect(selectedNodes.Any() ? selectedNodes : Nodes); + var nodesToUse = selectedNodes.Any() ? selectedNodes : Nodes; + (var minX, var maxX, var minY, var maxY) = nodesToUse.GetBounds(); var width = maxX - minX + 2 * margin; var height = maxY - minY + 2 * margin; minX -= margin; @@ -313,39 +315,6 @@ public void ZoomToFit(double margin = 10) UpdatePan(Container.Left - nx, Container.Top - ny); } - public (double minX, double maxX, double minY, double maxY) GetNodesRect(IEnumerable nodes) - { - var minX = double.MaxValue; - var maxX = double.MinValue; - var minY = double.MaxValue; - var maxY = double.MinValue; - - foreach (var node in nodes) - { - var trX = node.Position.X + node.Size!.Width; - var bY = node.Position.Y + node.Size.Height; - - if (node.Position.X < minX) - { - minX = node.Position.X; - } - if (trX > maxX) - { - maxX = trX; - } - if (node.Position.Y < minY) - { - minY = node.Position.Y; - } - if (bY > maxY) - { - maxY = bY; - } - } - - return (minX, maxX, minY, maxY); - } - public void UpdatePan(double deltaX, double deltaY) { Pan = Pan.Add(deltaX, deltaY); diff --git a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs new file mode 100644 index 00000000..e99b2615 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs @@ -0,0 +1,41 @@ +using Blazor.Diagrams.Core.Models; +using System.Collections.Generic; + +namespace Blazor.Diagrams.Core.Extensions +{ + public static class DiagramExtensions + { + public static (double minX, double maxX, double minY, double maxY) GetBounds(this IEnumerable nodes) + { + var minX = double.MaxValue; + var maxX = double.MinValue; + var minY = double.MaxValue; + var maxY = double.MinValue; + + foreach (var node in nodes) + { + var trX = node.Position.X + node.Size!.Width; + var bY = node.Position.Y + node.Size.Height; + + if (node.Position.X < minX) + { + minX = node.Position.X; + } + if (trX > maxX) + { + maxX = trX; + } + if (node.Position.Y < minY) + { + minY = node.Position.Y; + } + if (bY > maxY) + { + maxY = bY; + } + } + + return (minX, maxX, minY, maxY); + } + } +} diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs index 42543e7c..7113f521 100644 --- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs +++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs @@ -1,4 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using System.Collections.Generic; using System.Linq; @@ -7,20 +8,17 @@ namespace Blazor.Diagrams.Core.Models { public class GroupModel : NodeModel { - private readonly DiagramManager _diagramManager; - private readonly byte _padding; - - public GroupModel(DiagramManager diagramManager, NodeModel[] children, byte padding = 30) + public GroupModel(NodeModel[] children, byte padding = 30) { - _diagramManager = diagramManager; - _padding = padding; - Size = Size.Zero; Children = children; + Padding = padding; Initialize(); } public NodeModel[] Children { get; private set; } + public byte Padding { get; } + public IEnumerable HandledLinks => Children.SelectMany(c => c.AllLinks).Distinct(); public override void SetPosition(double x, double y) @@ -78,9 +76,9 @@ private bool UpdateDimensions() if (Children.Any(n => n.Size == null)) return false; - (var nodesMinX, var nodesMaxX, var nodesMinY, var nodesMaxY) = _diagramManager.GetNodesRect(Children); - Size = new Size(nodesMaxX - nodesMinX + _padding * 2, nodesMaxY - nodesMinY + _padding * 2); - Position = new Point(nodesMinX - _padding, nodesMinY - _padding); + (var nodesMinX, var nodesMaxX, var nodesMinY, var nodesMaxY) = Children.GetBounds(); + Size = new Size(nodesMaxX - nodesMinX + Padding * 2, nodesMaxY - nodesMinY + Padding * 2); + Position = new Point(nodesMinX - Padding, nodesMinY - Padding); return true; } } diff --git a/src/Blazor.Diagrams/Components/Navigator.razor.cs b/src/Blazor.Diagrams/Components/Navigator.razor.cs index 4c0ec29b..5c789fa0 100644 --- a/src/Blazor.Diagrams/Components/Navigator.razor.cs +++ b/src/Blazor.Diagrams/Components/Navigator.razor.cs @@ -1,4 +1,5 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Extensions; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; @@ -73,7 +74,7 @@ private void Refresh() if (nodes.Count == 0) return; - (var nodesMinX, var nodesMaxX, var nodesMinY, var nodesMaxY) = DiagramManager.GetNodesRect(nodes); + (var nodesMinX, var nodesMaxX, var nodesMinY, var nodesMaxY) = nodes.GetBounds(); nodesMinX *= DiagramManager.Zoom; nodesMaxX *= DiagramManager.Zoom; nodesMinY *= DiagramManager.Zoom; From 813e51d6f4257490a665ac5ade5e23846d5dc107 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sun, 21 Feb 2021 16:05:07 +0100 Subject: [PATCH 19/67] feat/refactor: Link/Group factories + Removed Color options --- .../Demos/DynamicInsertions.razor.cs | 6 ++--- samples/SharedDemo/Demos/Grouping.razor.cs | 6 ++--- samples/SharedDemo/ReflectionUtils.cs | 3 ++- .../Behaviors/DragNewLinkBehavior.cs | 4 +--- .../Behaviors/GroupingBehavior.cs | 8 +++---- src/Blazor.Diagrams.Core/Delegates.cs | 7 +++++- src/Blazor.Diagrams.Core/DiagramManager.cs | 2 +- src/Blazor.Diagrams.Core/DiagramOptions.cs | 23 +++++++++++++------ 8 files changed, 34 insertions(+), 25 deletions(-) diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs index b4448e71..59704e84 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs @@ -10,15 +10,13 @@ namespace SharedDemo.Demos public class DynamicInsertionsComponent : ComponentBase { private static readonly Random _random = new Random(); - protected readonly DiagramManager diagramManager = new DiagramManager(new DiagramOptions - { - GroupingEnabled = true - }); + protected readonly DiagramManager diagramManager = new DiagramManager(); protected override void OnInitialized() { base.OnInitialized(); + diagramManager.Options.Groups.Enabled = true; var node1 = new NodeModel(new Point(300, 50)); var node2 = new NodeModel(new Point(300, 400)); diagramManager.Nodes.Add(node1, node2); diff --git a/samples/SharedDemo/Demos/Grouping.razor.cs b/samples/SharedDemo/Demos/Grouping.razor.cs index 341e8ff0..7e450254 100644 --- a/samples/SharedDemo/Demos/Grouping.razor.cs +++ b/samples/SharedDemo/Demos/Grouping.razor.cs @@ -7,15 +7,13 @@ namespace SharedDemo.Demos { public class GroupingComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(new DiagramOptions - { - GroupingEnabled = true - }); + protected readonly DiagramManager diagramManager = new DiagramManager(); protected override void OnInitialized() { base.OnInitialized(); + diagramManager.Options.Groups.Enabled = true; var node1 = NewNode(50, 50); var node2 = NewNode(250, 250); var node3 = NewNode(500, 100); diff --git a/samples/SharedDemo/ReflectionUtils.cs b/samples/SharedDemo/ReflectionUtils.cs index c0c9980b..4de7d994 100644 --- a/samples/SharedDemo/ReflectionUtils.cs +++ b/samples/SharedDemo/ReflectionUtils.cs @@ -48,7 +48,8 @@ private static bool IsPrimitiveOrNullable(Type type) return type == typeof(object) || type == typeof(Type) || Type.GetTypeCode(type) != TypeCode.Object || - Nullable.GetUnderlyingType(type) != null; + Nullable.GetUnderlyingType(type) != null || + typeof(Delegate).IsAssignableFrom(type); } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index cab4ffb3..b5ce4287 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -26,11 +26,9 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) _initialX = e.ClientX; _initialY = e.ClientY; - // Todo: Link creator from Options - _ongoingLink = new LinkModel(port, null); + _ongoingLink = DiagramManager.Options.Links.Factory(DiagramManager, port); _ongoingLink.OnGoingPosition = new Point(port.Position.X + port.Size.Width / 2, port.Position.Y + port.Size.Height / 2); - _ongoingLink.TargetMarker = LinkMarker.Arrow; DiagramManager.Links.Add(_ongoingLink); } diff --git a/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs index 3592a531..f28f9237 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs @@ -1,5 +1,4 @@ -using Blazor.Diagrams.Core.Models; -using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.Web; using System; using System.Linq; @@ -14,13 +13,14 @@ public GroupingBehavior(DiagramManager diagramManager) : base(diagramManager) private void DiagramManager_KeyDown(KeyboardEventArgs e) { - if (!DiagramManager.Options.GroupingEnabled) + if (!DiagramManager.Options.Groups.Enabled) return; if (!DiagramManager.GetSelectedModels().Any()) return; - if (!e.CtrlKey || !e.AltKey || !e.Key.Equals("g", StringComparison.InvariantCultureIgnoreCase)) + Console.WriteLine(DiagramManager.Options.Groups.KeyboardShortcut(e)); + if (!DiagramManager.Options.Groups.KeyboardShortcut(e)) return; var selectedNodes = DiagramManager.Nodes.Where(n => n.Selected).ToArray(); diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs index cb4feed9..e0836b1b 100644 --- a/src/Blazor.Diagrams.Core/Delegates.cs +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -1,4 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; namespace Blazor.Diagrams.Core @@ -6,4 +7,8 @@ namespace Blazor.Diagrams.Core public delegate Point[] Router(DiagramManager diagram, BaseLinkModel link, Point from, Point to); public delegate PathGeneratorResult PathGenerator(DiagramManager diagram, BaseLinkModel link, Point[] route); + + public delegate BaseLinkModel LinkFactory(DiagramManager diagram, PortModel sourcePort); + + public delegate GroupModel GroupFactory(DiagramManager diagram, NodeModel[] children); } diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 05a44af3..9c93940d 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -127,7 +127,7 @@ public GroupModel Group(params NodeModel[] children) if (children.Any(n => n.Group != null)) throw new InvalidOperationException("Cannot group nodes that already belong to another group"); - var group = new GroupModel(children); + var group = Options.Groups.Factory(this, children); AddGroup(group); return group; } diff --git a/src/Blazor.Diagrams.Core/DiagramOptions.cs b/src/Blazor.Diagrams.Core/DiagramOptions.cs index 1c450981..efc60aac 100644 --- a/src/Blazor.Diagrams.Core/DiagramOptions.cs +++ b/src/Blazor.Diagrams.Core/DiagramOptions.cs @@ -1,4 +1,6 @@ -using System; +using Blazor.Diagrams.Core.Models; +using Microsoft.AspNetCore.Components.Web; +using System; using System.ComponentModel; namespace Blazor.Diagrams.Core @@ -11,8 +13,6 @@ public class DiagramOptions public Type? DefaultNodeComponent { get; set; } [Description("The grid size (grid-based snaping")] public int? GridSize { get; set; } - [Description("Whether to enable the ability to group nodes together using [CTRL+ALT+G] or not")] - public bool GroupingEnabled { get; set; } [Description("Whether to allow users to select multiple nodes at once using CTRL or not")] public bool AllowMultiSelection { get; set; } = true; [Description("Whether to allow panning or not")] @@ -22,16 +22,13 @@ public class DiagramOptions public DiagramZoomOptions Zoom { get; set; } = new DiagramZoomOptions(); public DiagramLinkOptions Links { get; set; } = new DiagramLinkOptions(); + public DiagramGroupOptions Groups { get; set; } = new DiagramGroupOptions(); } public class DiagramLinkOptions { [Description("The default component for links")] public Type? DefaultLinkComponent { get; set; } - [Description("The default color for links")] - public string DefaultColor { get; set; } = "black"; - [Description("The default color for selected links")] - public string DefaultSelectedColor { get; set; } = "rgb(110, 159, 212)"; [Description("Default Router for links")] public Router DefaultRouter { get; set; } = Routers.Normal; [Description("Default PathGenerator for links")] @@ -40,6 +37,8 @@ public class DiagramLinkOptions public bool EnableSnapping { get; set; } [Description("Link snapping radius")] public double SnappingRadius { get; set; } = 50; + [Description("Link model factory")] + public LinkFactory Factory { get; set; } = (diagram, sourcePort) => new LinkModel(sourcePort); } public class DiagramZoomOptions @@ -53,4 +52,14 @@ public class DiagramZoomOptions [Description("Maximum value allowed")] public double Maximum { get; set; } = 2; } + + public class DiagramGroupOptions + { + [Description("Whether to allow users to group/ungroup nodes")] + public bool Enabled { get; set; } + [Description("Keyboard shortcut (CTRL+ALT+G by default)")] + public Func KeyboardShortcut { get; set; } = e => e.CtrlKey && e.AltKey && e.Key == "g"; + [Description("Group model factory")] + public GroupFactory Factory { get; set; } = (diagram, children) => new GroupModel(children); + } } From b49fec80bb55c1d4ff1ff6056db76eff4addee2d Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 22 Feb 2021 00:34:19 +0100 Subject: [PATCH 20/67] fix: add back Color options --- src/Blazor.Diagrams.Core/DiagramOptions.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Blazor.Diagrams.Core/DiagramOptions.cs b/src/Blazor.Diagrams.Core/DiagramOptions.cs index efc60aac..5d7f6d7d 100644 --- a/src/Blazor.Diagrams.Core/DiagramOptions.cs +++ b/src/Blazor.Diagrams.Core/DiagramOptions.cs @@ -29,6 +29,10 @@ public class DiagramLinkOptions { [Description("The default component for links")] public Type? DefaultLinkComponent { get; set; } + [Description("The default color for links")] + public string DefaultColor { get; set; } = "black"; + [Description("The default color for selected links")] + public string DefaultSelectedColor { get; set; } = "rgb(110, 159, 212)"; [Description("Default Router for links")] public Router DefaultRouter { get; set; } = Routers.Normal; [Description("Default PathGenerator for links")] From 89878bf50aeac83f02129e9e6b9deaaea2f50d70 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Thu, 25 Feb 2021 15:04:14 +0100 Subject: [PATCH 21/67] feat: SelectionBoxWidget + rename GetRelativePoint --- samples/SharedDemo/Demos/DragAndDrop.razor.cs | 2 +- .../Behaviors/PanBehavior.cs | 3 +- src/Blazor.Diagrams.Core/DiagramManager.cs | 10 +- .../Models/Core/Rectangle.cs | 41 +++++++- src/Blazor.Diagrams.Core/Models/NodeModel.cs | 2 + .../Components/DiagramCanvas.razor | 4 +- .../Components/LinkWidget.razor.cs | 2 +- .../Components/SelectionBoxWidget.razor | 4 + .../Components/SelectionBoxWidget.razor.cs | 93 +++++++++++++++++++ 9 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 src/Blazor.Diagrams/Components/SelectionBoxWidget.razor create mode 100644 src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs diff --git a/samples/SharedDemo/Demos/DragAndDrop.razor.cs b/samples/SharedDemo/Demos/DragAndDrop.razor.cs index a23d1466..1d0a71de 100644 --- a/samples/SharedDemo/Demos/DragAndDrop.razor.cs +++ b/samples/SharedDemo/Demos/DragAndDrop.razor.cs @@ -31,7 +31,7 @@ private void OnDrop(DragEventArgs e) if (_draggedType == null) // Unkown item return; - var position = _diagramManager.GetRelativePoint(e.ClientX, e.ClientY); + var position = _diagramManager.GetRelativeMousePoint(e.ClientX, e.ClientY); var node = _draggedType == 0 ? new NodeModel(position) : new BotAnswerNode(position); node.AddPort(PortAlignment.Top); node.AddPort(PortAlignment.Bottom); diff --git a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs index be0e3126..46b5d060 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs @@ -1,6 +1,7 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; +using System; namespace Blazor.Diagrams.Core.Behaviors { @@ -19,7 +20,7 @@ public PanBehavior(DiagramManager diagramManager) : base(diagramManager) private void DiagramManager_MouseDown(Model model, MouseEventArgs e) { - if (!DiagramManager.Options.AllowPanning || model != null) + if (!DiagramManager.Options.AllowPanning || model != null || e.ShiftKey) return; _initialPan = DiagramManager.Pan; diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 9c93940d..5a13516b 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -336,7 +336,7 @@ internal void SetContainer(Rectangle newRect) Refresh(); } - public Point GetRelativePoint(double clientX, double clientY) + public Point GetRelativeMousePoint(double clientX, double clientY) { if (Container == null) throw new Exception("Container not available. Make sure you're not using this method before the diagram is fully loaded"); @@ -344,6 +344,14 @@ public Point GetRelativePoint(double clientX, double clientY) return new Point((clientX - Container.Left - Pan.X) / Zoom, (clientY - Container.Top - Pan.Y) / Zoom); } + public Point GetRelativePoint(double clientX, double clientY) + { + if (Container == null) + throw new Exception("Container not available. Make sure you're not using this method before the diagram is fully loaded"); + + return new Point(clientX - Container.Left, clientY - Container.Top); + } + internal void OnMouseDown(Model model, MouseEventArgs e) => MouseDown?.Invoke(model, e); internal void OnMouseMove(Model model, MouseEventArgs e) => MouseMove?.Invoke(model, e); diff --git a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs b/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs index 2986200d..a22afd64 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs @@ -1,9 +1,9 @@ -namespace Blazor.Diagrams.Core.Models.Core +using System; + +namespace Blazor.Diagrams.Core.Models.Core { public class Rectangle { - public double X { get; set; } - public double Y { get; set; } public double Width { get; set; } public double Height { get; set; } public double Top { get; set; } @@ -11,7 +11,40 @@ public class Rectangle public double Bottom { get; set; } public double Left { get; set; } + public Rectangle() + { + + } + + public Rectangle(double left, double top, double right, double bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + Width = Math.Abs(Left - Right); + Height = Math.Abs(Top - Bottom); + } + + public Rectangle(Point position, Size size) + { + Left = position.X; + Top = position.Y; + Right = Left + size.Width; + Bottom = Top + size.Height; + Width = size.Width; + Height = size.Height; + } + + public bool Overlap(Rectangle r) + => Left < r.Right && Right > r.Left && Top < r.Bottom && Bottom > r.Top; + + public bool ContainsPoint(Point point) => ContainsPoint(point.X, point.Y); + + public bool ContainsPoint(double x, double y) + => x >= Left && x <= Right && y >= Top && y <= Bottom; + public override string ToString() - => $"Rectangle(x={X}, y={Y}, width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})"; + => $"Rectangle(width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})"; } } diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index 86ee55e9..fcbfaf0b 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -92,6 +92,8 @@ public virtual void UpdatePositionSilently(double deltaX, double deltaY) Refresh(); } + public Rectangle? GetBounds() => Size == null ? null : new Rectangle(Position, Size); + private void UpdatePortPositions(double deltaX, double deltaY) { // Save some JS calls and update ports directly here diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index 0c6a8eda..976715a1 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -34,6 +34,6 @@ } - -@Widgets \ No newline at end of file + @Widgets + \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index 6f73ce02..f709d066 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -47,7 +47,7 @@ private void OnMouseDown(MouseEventArgs e, int index) if (!Link.Segmentable) return; - var rPt = DiagramManager.GetRelativePoint(e.ClientX, e.ClientY); + var rPt = DiagramManager.GetRelativeMousePoint(e.ClientX, e.ClientY); var vertex = new LinkVertexModel(Link, rPt); Link.Vertices.Insert(index, vertex); DiagramManager.OnMouseDown(vertex, e); diff --git a/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor new file mode 100644 index 00000000..4ad4e761 --- /dev/null +++ b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor @@ -0,0 +1,4 @@ +@if (_selectionBoxTopLeft != null && _selectionBoxSize != null) +{ +
+} \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs new file mode 100644 index 00000000..337b1a22 --- /dev/null +++ b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs @@ -0,0 +1,93 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Models.Core; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; +using System; + +namespace Blazor.Diagrams.Components +{ + public partial class SelectionBoxWidget : IDisposable + { + private Point _initialClientPoint; + private Point _selectionBoxTopLeft; + private Size _selectionBoxSize; + + [CascadingParameter(Name = "DiagramManager")] + public DiagramManager DiagramManager { get; set; } + + [Parameter] + public string Background { get; set; } = "rgb(110 159 212 / 25%);"; + + protected override void OnInitialized() + { + DiagramManager.MouseDown += OnMouseDown; + DiagramManager.MouseMove += OnMouseMove; + DiagramManager.MouseUp += OnMouseUp; + } + + private string GenerateStyle() + => FormattableString.Invariant($"position: absolute; background: {Background}; top: {_selectionBoxTopLeft.Y}px; left: {_selectionBoxTopLeft.X}px; width: {_selectionBoxSize.Width}px; height: {_selectionBoxSize.Height}px;"); + + private void OnMouseDown(Model model, MouseEventArgs e) + { + if (model != null || !e.ShiftKey) + return; + + _initialClientPoint = new Point(e.ClientX, e.ClientY); + } + + private void OnMouseMove(Model model, MouseEventArgs e) + { + if (_initialClientPoint == null) + return; + + SetSelectionBoxInformation(e); + + var start = DiagramManager.GetRelativeMousePoint(_initialClientPoint.X, _initialClientPoint.Y); + var end = DiagramManager.GetRelativeMousePoint(e.ClientX, e.ClientY); + (var sX, var sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)); + (var eX, var eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y)); + var bounds = new Rectangle(sX, sY, eX, eY); + + foreach (var node in DiagramManager.Nodes) + { + if (bounds.Overlap(node.GetBounds())) + { + DiagramManager.SelectModel(node, false); + } + else if (node.Selected) + { + DiagramManager.UnselectModel(node); + } + } + + StateHasChanged(); + } + + private void SetSelectionBoxInformation(MouseEventArgs e) + { + var start = DiagramManager.GetRelativePoint(_initialClientPoint.X, _initialClientPoint.Y); + var end = DiagramManager.GetRelativePoint(e.ClientX, e.ClientY); + (var sX, var sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)); + (var eX, var eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y)); + _selectionBoxTopLeft = new Point(sX, sY); + _selectionBoxSize = new Size(eX - sX, eY - sY); + } + + private void OnMouseUp(Model model, MouseEventArgs e) + { + _initialClientPoint = null; + _selectionBoxTopLeft = null; + _selectionBoxSize = null; + StateHasChanged(); + } + + public void Dispose() + { + DiagramManager.MouseDown -= OnMouseDown; + DiagramManager.MouseMove -= OnMouseMove; + DiagramManager.MouseUp -= OnMouseUp; + } + } +} From 998cabc30598db278039e0192a4749a753f2e132 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Thu, 25 Feb 2021 17:33:13 +0100 Subject: [PATCH 22/67] refactor: rename Navigator to NavigatorWidget --- README.md | 2 +- .../Demos/Algorithms/ReconnectLinksToClosestPorts.razor | 2 +- samples/SharedDemo/Demos/Grouping.razor | 2 +- samples/SharedDemo/Demos/SnapToGrid.razor | 2 +- .../Components/{Navigator.razor => NavigatorWidget.razor} | 4 +--- .../{Navigator.razor.cs => NavigatorWidget.razor.cs} | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) rename src/Blazor.Diagrams/Components/{Navigator.razor => NavigatorWidget.razor} (97%) rename src/Blazor.Diagrams/Components/{Navigator.razor.cs => NavigatorWidget.razor.cs} (99%) diff --git a/README.md b/README.md index b9ac3472..0062d8eb 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ You can get started very easily & quickly using: - + diff --git a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor index cd0847d0..0891d52b 100644 --- a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor +++ b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor @@ -21,7 +21,7 @@ - + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Grouping.razor b/samples/SharedDemo/Demos/Grouping.razor index 38ddaa8d..fd7e2e4c 100644 --- a/samples/SharedDemo/Demos/Grouping.razor +++ b/samples/SharedDemo/Demos/Grouping.razor @@ -18,7 +18,7 @@ - + diff --git a/samples/SharedDemo/Demos/SnapToGrid.razor b/samples/SharedDemo/Demos/SnapToGrid.razor index b75e80f2..e2af2ff7 100644 --- a/samples/SharedDemo/Demos/SnapToGrid.razor +++ b/samples/SharedDemo/Demos/SnapToGrid.razor @@ -18,7 +18,7 @@ - + diff --git a/src/Blazor.Diagrams/Components/Navigator.razor b/src/Blazor.Diagrams/Components/NavigatorWidget.razor similarity index 97% rename from src/Blazor.Diagrams/Components/Navigator.razor rename to src/Blazor.Diagrams/Components/NavigatorWidget.razor index 1ae08e71..6872461e 100644 --- a/src/Blazor.Diagrams/Components/Navigator.razor +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor @@ -1,6 +1,4 @@ -@inherits NavigatorComponent - -@if (DiagramManager.Container != null) +@if (DiagramManager.Container != null) { var addedNodeX = Math.Max(0, DiagramManager.Pan.X); var addedNodeY = Math.Max(0, DiagramManager.Pan.Y); diff --git a/src/Blazor.Diagrams/Components/Navigator.razor.cs b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs similarity index 99% rename from src/Blazor.Diagrams/Components/Navigator.razor.cs rename to src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs index 5c789fa0..57fc13a8 100644 --- a/src/Blazor.Diagrams/Components/Navigator.razor.cs +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs @@ -8,7 +8,7 @@ namespace Blazor.Diagrams.Components { - public class NavigatorComponent : ComponentBase, IDisposable + public partial class NavigatorWidget : IDisposable { [CascadingParameter(Name = "DiagramManager")] From 5e182caafa4855dc9d23eb49b40d7fdd249986f6 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 26 Feb 2021 22:51:14 +0100 Subject: [PATCH 23/67] refactor: GetBounds(nodes) returns a Rectangle now --- samples/SharedDemo/Demos/Simple.razor | 2 +- src/Blazor.Diagrams.Core/DiagramManager.cs | 12 ++++++------ .../Extensions/DiagramExtensions.cs | 5 +++-- src/Blazor.Diagrams.Core/Models/GroupModel.cs | 6 +++--- .../Components/NavigatorWidget.razor.cs | 10 +++++----- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/samples/SharedDemo/Demos/Simple.razor b/samples/SharedDemo/Demos/Simple.razor index 225cae41..fb56f764 100644 --- a/samples/SharedDemo/Demos/Simple.razor +++ b/samples/SharedDemo/Demos/Simple.razor @@ -32,7 +32,7 @@ - + diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/DiagramManager.cs index 5a13516b..6af59fad 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/DiagramManager.cs @@ -205,7 +205,7 @@ public IEnumerable GetSelectedModels() { if (vertex.Selected) yield return vertex; - } + } } foreach (var group in Groups) @@ -300,11 +300,11 @@ public void ZoomToFit(double margin = 10) var selectedNodes = Nodes.Where(s => s.Selected); var nodesToUse = selectedNodes.Any() ? selectedNodes : Nodes; - (var minX, var maxX, var minY, var maxY) = nodesToUse.GetBounds(); - var width = maxX - minX + 2 * margin; - var height = maxY - minY + 2 * margin; - minX -= margin; - minY -= margin; + var bounds = nodesToUse.GetBounds(); + var width = bounds.Width + 2 * margin; + var height = bounds.Height + 2 * margin; + var minX = bounds.Left - margin; + var minY = bounds.Top - margin; var xf = Container.Width / width; var yf = Container.Height / height; diff --git a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs index e99b2615..14a6a211 100644 --- a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs +++ b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs @@ -1,11 +1,12 @@ using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; using System.Collections.Generic; namespace Blazor.Diagrams.Core.Extensions { public static class DiagramExtensions { - public static (double minX, double maxX, double minY, double maxY) GetBounds(this IEnumerable nodes) + public static Rectangle GetBounds(this IEnumerable nodes) { var minX = double.MaxValue; var maxX = double.MinValue; @@ -35,7 +36,7 @@ public static (double minX, double maxX, double minY, double maxY) GetBounds(thi } } - return (minX, maxX, minY, maxY); + return new Rectangle(minX, minY, maxX, maxY); } } } diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs index 7113f521..fb7ca6c9 100644 --- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs +++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs @@ -76,9 +76,9 @@ private bool UpdateDimensions() if (Children.Any(n => n.Size == null)) return false; - (var nodesMinX, var nodesMaxX, var nodesMinY, var nodesMaxY) = Children.GetBounds(); - Size = new Size(nodesMaxX - nodesMinX + Padding * 2, nodesMaxY - nodesMinY + Padding * 2); - Position = new Point(nodesMinX - Padding, nodesMinY - Padding); + var bounds = Children.GetBounds(); + Size = new Size(bounds.Width + Padding * 2, bounds.Height + Padding * 2); + Position = new Point(bounds.Left - Padding, bounds.Top - Padding); return true; } } diff --git a/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs index 57fc13a8..08b61939 100644 --- a/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs @@ -74,11 +74,11 @@ private void Refresh() if (nodes.Count == 0) return; - (var nodesMinX, var nodesMaxX, var nodesMinY, var nodesMaxY) = nodes.GetBounds(); - nodesMinX *= DiagramManager.Zoom; - nodesMaxX *= DiagramManager.Zoom; - nodesMinY *= DiagramManager.Zoom; - nodesMaxY *= DiagramManager.Zoom; + var bounds = nodes.GetBounds(); + var nodesMinX = bounds.Left * DiagramManager.Zoom; + var nodesMaxX = bounds.Right * DiagramManager.Zoom; + var nodesMinY = bounds.Top * DiagramManager.Zoom; + var nodesMaxY = bounds.Bottom * DiagramManager.Zoom; (double fullSizeWidth, double fullSizeHeight) = GetFullSize(nodesMaxX, nodesMaxY); AdjustFullSizeWithNodesRect(nodesMinX, nodesMinY, ref fullSizeWidth, ref fullSizeHeight); From 15648e28ea3888ea1ec77741ed15167f82573245 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 26 Feb 2021 22:54:30 +0100 Subject: [PATCH 24/67] fix: Take into account zoom when Node is re-visible and Size is received --- src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index 06351649..39b6b396 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -49,6 +49,7 @@ public void OnResize(Size size) if (Size.Zero.Equals(size)) return; + size = new Size(size.Width / DiagramManager.Zoom, size.Height / DiagramManager.Zoom); if (size.Equals(Node.Size)) return; From 3ed7d4598680eb313538b8143bf1fc5f43d87ae8 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 26 Feb 2021 23:47:24 +0100 Subject: [PATCH 25/67] fix: Only start dragging new link on LEFT click --- .../Behaviors/DragNewLinkBehavior.cs | 3 ++- src/Blazor.Diagrams.Core/MouseEventButton.cs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/Blazor.Diagrams.Core/MouseEventButton.cs diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index b5ce4287..cd77aa82 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -2,6 +2,7 @@ using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; +using System; using System.Linq; namespace Blazor.Diagrams.Core.Behaviors @@ -21,7 +22,7 @@ public DragNewLinkBehavior(DiagramManager diagramManager) : base(diagramManager) private void DiagramManager_MouseDown(Model model, MouseEventArgs e) { - if (!(model is PortModel port) || port.Locked) + if (!(model is PortModel port) || port.Locked || e.Button != (int)MouseEventButton.Left) return; _initialX = e.ClientX; diff --git a/src/Blazor.Diagrams.Core/MouseEventButton.cs b/src/Blazor.Diagrams.Core/MouseEventButton.cs new file mode 100644 index 00000000..98d353a2 --- /dev/null +++ b/src/Blazor.Diagrams.Core/MouseEventButton.cs @@ -0,0 +1,11 @@ +namespace Blazor.Diagrams.Core +{ + public enum MouseEventButton : long + { + Left = 0, + Wheel = 1, + Right = 2, + Fourth = 4, + Fifth = 5 + } +} From 7332cc11f06f407a183a375ee915694ad412e1f6 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 27 Feb 2021 00:09:40 +0100 Subject: [PATCH 26/67] refactor: Rename DiagramManager to Diagram & Remove need to specify Name in CascadingValue --- samples/SharedDemo/Customization.razor | 54 +++++----- .../ReconnectLinksToClosestPorts.razor | 2 +- .../ReconnectLinksToClosestPorts.razor.cs | 10 +- .../SharedDemo/Demos/CustomGroup/Demo.razor | 2 +- .../Demos/CustomGroup/Demo.razor.cs | 12 +-- .../SharedDemo/Demos/CustomLink/Demo.razor | 2 +- .../SharedDemo/Demos/CustomLink/Demo.razor.cs | 12 +-- samples/SharedDemo/Demos/CustomNode.razor | 2 +- samples/SharedDemo/Demos/CustomNode.razor.cs | 6 +- .../SharedDemo/Demos/CustomPort/Demo.razor | 2 +- .../SharedDemo/Demos/CustomPort/Demo.razor.cs | 8 +- samples/SharedDemo/Demos/DragAndDrop.razor | 2 +- samples/SharedDemo/Demos/DragAndDrop.razor.cs | 8 +- .../SharedDemo/Demos/DynamicInsertions.razor | 2 +- .../Demos/DynamicInsertions.razor.cs | 26 ++--- samples/SharedDemo/Demos/Events.razor | 4 +- samples/SharedDemo/Demos/Events.razor.cs | 24 ++--- samples/SharedDemo/Demos/Grouping.razor | 2 +- samples/SharedDemo/Demos/Grouping.razor.cs | 12 +-- samples/SharedDemo/Demos/Locked.razor | 2 +- samples/SharedDemo/Demos/Locked.razor.cs | 6 +- samples/SharedDemo/Demos/Performance.razor | 2 +- samples/SharedDemo/Demos/Performance.razor.cs | 6 +- samples/SharedDemo/Demos/Simple.razor | 2 +- samples/SharedDemo/Demos/SnapToGrid.razor | 2 +- samples/SharedDemo/Demos/SnapToGrid.razor.cs | 6 +- samples/SharedDemo/Demos/ZoomToFit.razor | 4 +- samples/SharedDemo/Demos/ZoomToFit.razor.cs | 6 +- samples/SharedDemo/Options.razor | 2 +- samples/SharedDemo/QuickStart.razor | 18 ++-- .../LinksReconnectionAlgorithms.cs | 4 +- src/Blazor.Diagrams.Core/Behavior.cs | 6 +- .../Behaviors/DebugEventsBehavior.cs | 72 +++++++------- .../Behaviors/DeleteSelectionBehavior.cs | 18 ++-- .../Behaviors/DragMovablesBehavior.cs | 32 +++--- .../Behaviors/DragNewLinkBehavior.cs | 36 +++---- .../Behaviors/EventsBehavior.cs | 16 +-- .../Behaviors/GroupingBehavior.cs | 22 ++--- .../Behaviors/PanBehavior.cs | 34 +++---- .../Behaviors/SelectionBehavior.cs | 14 +-- .../Behaviors/ZoomBehavior.cs | 40 ++++---- src/Blazor.Diagrams.Core/Delegates.cs | 8 +- .../{DiagramManager.cs => Diagram.cs} | 4 +- .../PathGenerators/PathGenerators.Smooth.cs | 2 +- .../PathGenerators/PathGenerators.Straight.cs | 2 +- .../Routers/Routers.Normal.cs | 2 +- .../Components/DiagramCanvas.razor | 8 +- .../Components/DiagramCanvas.razor.cs | 24 ++--- .../Components/Groups/GroupContainer.razor.cs | 8 +- .../Components/LinkVertexWidget.razor.cs | 6 +- .../Components/LinkWidget.razor | 16 +-- .../Components/LinkWidget.razor.cs | 8 +- .../Components/NavigatorWidget.razor | 34 +++---- .../Components/NavigatorWidget.razor.cs | 98 +++++++++---------- .../Components/Renderers/GroupRenderer.cs | 6 +- .../Components/Renderers/LinkLabelRenderer.cs | 4 +- .../Components/Renderers/LinkRenderer.cs | 12 +-- .../Components/Renderers/NodeRenderer.cs | 40 ++++---- .../Renderers/PortRenderer.razor.cs | 16 +-- .../Components/SelectionBoxWidget.razor.cs | 30 +++--- 60 files changed, 435 insertions(+), 435 deletions(-) rename src/Blazor.Diagrams.Core/{DiagramManager.cs => Diagram.cs} (99%) diff --git a/samples/SharedDemo/Customization.razor b/samples/SharedDemo/Customization.razor index 03ebe2fd..2686603b 100644 --- a/samples/SharedDemo/Customization.razor +++ b/samples/SharedDemo/Customization.razor @@ -73,16 +73,16 @@

Registering the custom node

- Lastly, we'll register the custom node and its component in the DiagramManager: + Lastly, we'll register the custom node and its component in the Diagram:

-
private DiagramManager DiagramManager { get; private set; }
+                
private Diagram Diagram { get; private set; }
 
 protected override void OnInitialized()
 {
     base.OnInitialized();
 
-    DiagramManager = new DiagramManager();
-    DiagramManager.RegisterModelComponent<AddTwoNumbersNode, AddTwoNumbersComponent>();
+    Diagram = new Diagram();
+    Diagram.RegisterModelComponent<AddTwoNumbersNode, AddTwoNumbersComponent>();
 }

This tells the Diagram manager to render a AddTwoNumbersComponent for every node of type AddTwoNumbersNode.

@@ -169,21 +169,21 @@ protected override void OnInitialized()

Lastly, we'll put everything together:

-
private readonly DiagramManager _diagramManager = new DiagramManager();
+                
private readonly Diagram _diagram = new Diagram();
 
 protected override void OnInitialized()
 {
     base.OnInitialized();
 
-    _diagramManager.Options.DefaultNodeComponent = typeof(ColoredNodeWidget);
+    _diagram.Options.DefaultNodeComponent = typeof(ColoredNodeWidget);
 
     var node1 = NewNode(50, 50);
     var node2 = NewNode(300, 300);
-    _diagramManager.AddNode(node1);
-    _diagramManager.AddNode(node2);
-    _diagramManager.AddNode(NewNode(500, 50));
+    _diagram.AddNode(node1);
+    _diagram.AddNode(node2);
+    _diagram.AddNode(NewNode(500, 50));
 
-    _diagramManager.AddLink(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top));
+    _diagram.AddLink(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top));
 }
 
 private NodeModel NewNode(double x, double y)
@@ -240,25 +240,25 @@ private NodeModel NewNode(double x, double y)
             
         
@@ -289,8 +289,8 @@ private NodeModel NewNode(double x, double y)
                 

public class CustomGroupModel : GroupModel
 {
-    public CustomGroupModel(DiagramManager diagramManager, NodeModel[] children, string title, byte padding = 30)
-        : base(diagramManager, children, padding)
+    public CustomGroupModel(Diagram diagram, NodeModel[] children, string title, byte padding = 30)
+        : base(diagram, children, padding)
     {
         Title = title;
     }
@@ -352,25 +352,25 @@ private NodeModel NewNode(double x, double y)
             

Registering the custom group

- Lastly, we'll register the custom link and its component in the DiagramManager: + Lastly, we'll register the custom link and its component in the Diagram:

protected override void OnInitialized()
 {
     base.OnInitialized();
                     
-    _diagramManager.RegisterModelComponent<CustomGroupModel, CustomGroupWidget>();
+    _diagram.RegisterModelComponent<CustomGroupModel, CustomGroupWidget>();
 
     var node1 = NewNode(50, 50);
     var node2 = NewNode(300, 300);
     var node3 = NewNode(500, 100);
 
-    _diagramManager.AddNode(node1);
+    _diagram.AddNode(node1);
     // You can create your own group directly and add it
     // You don't have to add its children to the diagram
-    _diagramManager.AddGroup(new CustomGroupModel(_diagramManager, new[] { node2, node3 }, "Group 1"));
+    _diagram.AddGroup(new CustomGroupModel(_diagram, new[] { node2, node3 }, "Group 1"));
 
-    _diagramManager.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left));
-    _diagramManager.AddLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Bottom));
+    _diagram.AddLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left));
+    _diagram.AddLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Bottom));
 }
 
 private NodeModel NewNode(double x, double y)
diff --git a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor
index 0891d52b..9d4b341e 100644
--- a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor
+++ b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor
@@ -18,7 +18,7 @@
     
 
- + diff --git a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs index bfaac7fa..a9c16490 100644 --- a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs +++ b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs @@ -8,7 +8,7 @@ namespace SharedDemo.Demos.Algorithms { public class ReconnectLinksToClosestPortsComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { @@ -17,13 +17,13 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(300, 50); - diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Right))); - diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Bottom), node3.GetPort(PortAlignment.Top))); - diagramManager.Nodes.Add(node1, node2, node3); + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Right))); + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Bottom), node3.GetPort(PortAlignment.Top))); + diagram.Nodes.Add(node1, node2, node3); } - protected void ReconnectLinks() => diagramManager.ReconnectLinksToClosestPorts(); + protected void ReconnectLinks() => diagram.ReconnectLinksToClosestPorts(); private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomGroup/Demo.razor b/samples/SharedDemo/Demos/CustomGroup/Demo.razor index f7933706..cff05d4d 100644 --- a/samples/SharedDemo/Demos/CustomGroup/Demo.razor +++ b/samples/SharedDemo/Demos/CustomGroup/Demo.razor @@ -2,6 +2,6 @@ @layout DemoLayout @inject LayoutData LayoutData - + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs index 2c82e19a..9a329633 100644 --- a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs @@ -6,7 +6,7 @@ namespace SharedDemo.Demos.CustomGroup { partial class Demo { - private readonly DiagramManager _diagramManager = new DiagramManager(); + private readonly Diagram _diagram = new Diagram(); protected override void OnInitialized() { @@ -16,17 +16,17 @@ protected override void OnInitialized() LayoutData.Info = "Creating your own custom groups is very easy!"; LayoutData.DataChanged(); - _diagramManager.RegisterModelComponent(); + _diagram.RegisterModelComponent(); var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(500, 100); - _diagramManager.Nodes.Add(node1, node2, node3); - _diagramManager.AddGroup(new CustomGroupModel(new[] { node2, node3 }, "Group 1")); + _diagram.Nodes.Add(node1, node2, node3); + _diagram.AddGroup(new CustomGroupModel(new[] { node2, node3 }, "Group 1")); - _diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - _diagramManager.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); + _diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + _diagram.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomLink/Demo.razor b/samples/SharedDemo/Demos/CustomLink/Demo.razor index fedc439a..bd20c21c 100644 --- a/samples/SharedDemo/Demos/CustomLink/Demo.razor +++ b/samples/SharedDemo/Demos/CustomLink/Demo.razor @@ -2,6 +2,6 @@ @layout DemoLayout @inject LayoutData LayoutData - + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs index 565b8e20..9e04bc6a 100644 --- a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs @@ -6,7 +6,7 @@ namespace SharedDemo.Demos.CustomLink { partial class Demo { - private readonly DiagramManager _diagramManager = new DiagramManager(); + private readonly Diagram _diagram = new Diagram(); protected override void OnInitialized() { @@ -16,16 +16,16 @@ protected override void OnInitialized() LayoutData.Info = "Creating your own custom links is very easy!"; LayoutData.DataChanged(); - _diagramManager.RegisterModelComponent(); - // Also usable: _diagramManager.Options.Links.DefaultLinkComponent = typeof(ThickLink); + _diagram.RegisterModelComponent(); + // Also usable: _diagram.Options.Links.DefaultLinkComponent = typeof(ThickLink); var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(500, 50); - _diagramManager.Nodes.Add(node1, node2, node3); - _diagramManager.Links.Add(new ThickLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - _diagramManager.Links.Add(new ThickLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); + _diagram.Nodes.Add(node1, node2, node3); + _diagram.Links.Add(new ThickLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + _diagram.Links.Add(new ThickLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomNode.razor b/samples/SharedDemo/Demos/CustomNode.razor index 3d38be5e..e5992c69 100644 --- a/samples/SharedDemo/Demos/CustomNode.razor +++ b/samples/SharedDemo/Demos/CustomNode.razor @@ -14,6 +14,6 @@ } } - + diff --git a/samples/SharedDemo/Demos/CustomNode.razor.cs b/samples/SharedDemo/Demos/CustomNode.razor.cs index 4fa9abdb..87195736 100644 --- a/samples/SharedDemo/Demos/CustomNode.razor.cs +++ b/samples/SharedDemo/Demos/CustomNode.razor.cs @@ -7,13 +7,13 @@ namespace SharedDemo.Demos { public class CustomNodeComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { base.OnInitialized(); - diagramManager.RegisterModelComponent(); + diagram.RegisterModelComponent(); var node = new NodeModel(new Point(20, 20)); node.AddPort(PortAlignment.Top); @@ -21,7 +21,7 @@ protected override void OnInitialized() node.AddPort(PortAlignment.Bottom); node.AddPort(PortAlignment.Left); - diagramManager.Nodes.Add(node, NewNode(100, 100), NewNode(300, 300)); + diagram.Nodes.Add(node, NewNode(100, 100), NewNode(300, 300)); } private BotAnswerNode NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomPort/Demo.razor b/samples/SharedDemo/Demos/CustomPort/Demo.razor index 8b20251e..b8675208 100644 --- a/samples/SharedDemo/Demos/CustomPort/Demo.razor +++ b/samples/SharedDemo/Demos/CustomPort/Demo.razor @@ -2,6 +2,6 @@ @layout DemoLayout @inject LayoutData LayoutData - + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs index b866b4d4..a23b6279 100644 --- a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs @@ -6,7 +6,7 @@ namespace SharedDemo.Demos.CustomPort { partial class Demo { - private readonly DiagramManager _diagramManager = new DiagramManager(); + private readonly Diagram _diagram = new Diagram(); protected override void OnInitialized() { @@ -17,12 +17,12 @@ protected override void OnInitialized() "In this example, you can only attach links from/to ports with the same color."; LayoutData.DataChanged(); - _diagramManager.Options.DefaultNodeComponent = typeof(ColoredNodeWidget); + _diagram.Options.DefaultNodeComponent = typeof(ColoredNodeWidget); var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - _diagramManager.Nodes.Add(node1, node2, NewNode(500, 50)); - _diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top))); + _diagram.Nodes.Add(node1, node2, NewNode(500, 50)); + _diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/DragAndDrop.razor b/samples/SharedDemo/Demos/DragAndDrop.razor index eb551b5e..0e1dd377 100644 --- a/samples/SharedDemo/Demos/DragAndDrop.razor +++ b/samples/SharedDemo/Demos/DragAndDrop.razor @@ -21,7 +21,7 @@
- +
diff --git a/samples/SharedDemo/Demos/DragAndDrop.razor.cs b/samples/SharedDemo/Demos/DragAndDrop.razor.cs index 1d0a71de..c5be42c4 100644 --- a/samples/SharedDemo/Demos/DragAndDrop.razor.cs +++ b/samples/SharedDemo/Demos/DragAndDrop.razor.cs @@ -6,7 +6,7 @@ namespace SharedDemo.Demos { public partial class DragAndDrop { - private readonly DiagramManager _diagramManager = new DiagramManager(); + private readonly Diagram _diagram = new Diagram(); private int? _draggedType; protected override void OnInitialized() @@ -17,7 +17,7 @@ protected override void OnInitialized() LayoutData.Info = "A very simple drag & drop implementation using the HTML5 events."; LayoutData.DataChanged(); - _diagramManager.RegisterModelComponent(); + _diagram.RegisterModelComponent(); } private void OnDragStart(int key) @@ -31,11 +31,11 @@ private void OnDrop(DragEventArgs e) if (_draggedType == null) // Unkown item return; - var position = _diagramManager.GetRelativeMousePoint(e.ClientX, e.ClientY); + var position = _diagram.GetRelativeMousePoint(e.ClientX, e.ClientY); var node = _draggedType == 0 ? new NodeModel(position) : new BotAnswerNode(position); node.AddPort(PortAlignment.Top); node.AddPort(PortAlignment.Bottom); - _diagramManager.Nodes.Add(node); + _diagram.Nodes.Add(node); _draggedType = null; } } diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor b/samples/SharedDemo/Demos/DynamicInsertions.razor index be4282dc..252882cf 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor @@ -22,6 +22,6 @@ - + diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs index 59704e84..bd1fea1e 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs @@ -10,34 +10,34 @@ namespace SharedDemo.Demos public class DynamicInsertionsComponent : ComponentBase { private static readonly Random _random = new Random(); - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { base.OnInitialized(); - diagramManager.Options.Groups.Enabled = true; + diagram.Options.Groups.Enabled = true; var node1 = new NodeModel(new Point(300, 50)); var node2 = new NodeModel(new Point(300, 400)); - diagramManager.Nodes.Add(node1, node2); + diagram.Nodes.Add(node1, node2); } protected void AddNode() { - var x = _random.Next(0, (int)diagramManager.Container.Width - 120); - var y = _random.Next(0, (int)diagramManager.Container.Height - 100); - diagramManager.Nodes.Add(new NodeModel(new Point(x, y))); + var x = _random.Next(0, (int)diagram.Container.Width - 120); + var y = _random.Next(0, (int)diagram.Container.Height - 100); + diagram.Nodes.Add(new NodeModel(new Point(x, y))); } protected void RemoveNode() { - var i = _random.Next(0, diagramManager.Nodes.Count); - diagramManager.Nodes.Remove(diagramManager.Nodes[i]); + var i = _random.Next(0, diagram.Nodes.Count); + diagram.Nodes.Remove(diagram.Nodes[i]); } protected void AddPort() { - var node = diagramManager.Nodes.FirstOrDefault(n => n.Selected); + var node = diagram.Nodes.FirstOrDefault(n => n.Selected); if (node == null) return; @@ -54,7 +54,7 @@ protected void AddPort() protected void RemovePort() { - var node = diagramManager.Nodes.FirstOrDefault(n => n.Selected); + var node = diagram.Nodes.FirstOrDefault(n => n.Selected); if (node == null) return; @@ -64,14 +64,14 @@ protected void RemovePort() var i = _random.Next(0, node.Ports.Count); var port = node.Ports[i]; - diagramManager.Links.Remove(port.Links.ToArray()); + diagram.Links.Remove(port.Links.ToArray()); node.RemovePort(port); node.Refresh(); } protected void AddLink() { - var selectedNodes = diagramManager.Nodes.Where(n => n.Selected).ToArray(); + var selectedNodes = diagram.Nodes.Where(n => n.Selected).ToArray(); if (selectedNodes.Length != 2) return; @@ -83,7 +83,7 @@ protected void AddLink() var sourcePort = node1.Ports[_random.Next(0, node1.Ports.Count)]; var targetPort = node2.Ports[_random.Next(0, node2.Ports.Count)]; - diagramManager.Links.Add(new LinkModel(sourcePort, targetPort)); + diagram.Links.Add(new LinkModel(sourcePort, targetPort)); } } } diff --git a/samples/SharedDemo/Demos/Events.razor b/samples/SharedDemo/Demos/Events.razor index 36fd8069..938ccd34 100644 --- a/samples/SharedDemo/Demos/Events.razor +++ b/samples/SharedDemo/Demos/Events.razor @@ -10,13 +10,13 @@ LayoutData.Title = "Events"; LayoutData.Info = "The current available events are: NodeAdded, NodeRemoved, SelectionChanged, LinkAdded, LinkAttached and LinkRemoved.
" + - "Also, the DiagramManager and every Model have a Changed event."; + "Also, the Diagram and every Model have a Changed event."; LayoutData.DataChanged(); } }
- +
diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index 64386ce9..b129b19b 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -9,7 +9,7 @@ namespace SharedDemo.Demos { public class EventsComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected readonly List events = new List(); protected override void OnInitialized() @@ -20,45 +20,45 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); - diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagram.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); } private void RegisterEvents() { - diagramManager.Changed += () => + diagram.Changed += () => { events.Add("Changed"); StateHasChanged(); }; - diagramManager.Nodes.Added += (nodes) => events.AddRange(nodes.Select(n => $"NodesAdded, NodeId={n.Id}")); - diagramManager.Nodes.Removed += (nodes) => events.AddRange(nodes.Select(n => $"NodesRemoved, NodeId={n.Id}")); + diagram.Nodes.Added += (nodes) => events.AddRange(nodes.Select(n => $"NodesAdded, NodeId={n.Id}")); + diagram.Nodes.Removed += (nodes) => events.AddRange(nodes.Select(n => $"NodesRemoved, NodeId={n.Id}")); - diagramManager.SelectionChanged += (m) => + diagram.SelectionChanged += (m) => { events.Add($"SelectionChanged, Id={m.Id}, Type={m.GetType().Name}, Selected={m.Selected}"); StateHasChanged(); }; - diagramManager.Links.Added += (links) => events.AddRange(links.Select(l => $"Links.Added, LinkId={l.Id}")); + diagram.Links.Added += (links) => events.AddRange(links.Select(l => $"Links.Added, LinkId={l.Id}")); // Todo: replace with TargetPortChanged - //diagramManager.LinkAttached += (l) => + //diagram.LinkAttached += (l) => //{ // events.Add($"LinkAttached, LinkId={l.Id}"); // StateHasChanged(); //}; - diagramManager.Links.Removed += (links) => events.AddRange(links.Select(l => $"Links.Removed, LinkId={l.Id}")); + diagram.Links.Removed += (links) => events.AddRange(links.Select(l => $"Links.Removed, LinkId={l.Id}")); - diagramManager.MouseUp += (m, e) => + diagram.MouseUp += (m, e) => { events.Add($"MouseUp, Type={m?.GetType().Name}, ModelId={m?.Id}"); StateHasChanged(); }; - diagramManager.MouseClick += (m, e) => + diagram.MouseClick += (m, e) => { events.Add($"MouseClick, Type={m?.GetType().Name}, ModelId={m?.Id}"); StateHasChanged(); diff --git a/samples/SharedDemo/Demos/Grouping.razor b/samples/SharedDemo/Demos/Grouping.razor index fd7e2e4c..b906acd3 100644 --- a/samples/SharedDemo/Demos/Grouping.razor +++ b/samples/SharedDemo/Demos/Grouping.razor @@ -15,7 +15,7 @@ } } - + diff --git a/samples/SharedDemo/Demos/Grouping.razor.cs b/samples/SharedDemo/Demos/Grouping.razor.cs index 7e450254..2d661fa4 100644 --- a/samples/SharedDemo/Demos/Grouping.razor.cs +++ b/samples/SharedDemo/Demos/Grouping.razor.cs @@ -7,26 +7,26 @@ namespace SharedDemo.Demos { public class GroupingComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { base.OnInitialized(); - diagramManager.Options.Groups.Enabled = true; + diagram.Options.Groups.Enabled = true; var node1 = NewNode(50, 50); var node2 = NewNode(250, 250); var node3 = NewNode(500, 100); - diagramManager.Nodes.Add(node1, node2, node3); + diagram.Nodes.Add(node1, node2, node3); - var group = diagramManager.Group(node1, node2); + var group = diagram.Group(node1, node2); group.AddPort(PortAlignment.Bottom); group.AddPort(PortAlignment.Top); group.AddPort(PortAlignment.Left); group.AddPort(PortAlignment.Right); - diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagramManager.Links.Add(new LinkModel(group.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagram.Links.Add(new LinkModel(group.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/Locked.razor b/samples/SharedDemo/Demos/Locked.razor index bde09cd0..05d122e2 100644 --- a/samples/SharedDemo/Demos/Locked.razor +++ b/samples/SharedDemo/Demos/Locked.razor @@ -16,6 +16,6 @@ } } - + diff --git a/samples/SharedDemo/Demos/Locked.razor.cs b/samples/SharedDemo/Demos/Locked.razor.cs index de3564fb..1621dbe4 100644 --- a/samples/SharedDemo/Demos/Locked.razor.cs +++ b/samples/SharedDemo/Demos/Locked.razor.cs @@ -7,7 +7,7 @@ namespace SharedDemo { public class LockedComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { @@ -15,13 +15,13 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Nodes.Add(node1, node2, NewNode(300, 50)); var link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) { Locked = true }; - diagramManager.Links.Add(link); + diagram.Links.Add(link); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/Performance.razor b/samples/SharedDemo/Demos/Performance.razor index 27bf8355..c3c45b85 100644 --- a/samples/SharedDemo/Demos/Performance.razor +++ b/samples/SharedDemo/Demos/Performance.razor @@ -14,6 +14,6 @@ } } - + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Performance.razor.cs b/samples/SharedDemo/Demos/Performance.razor.cs index 6fb7cc1d..bcb01fca 100644 --- a/samples/SharedDemo/Demos/Performance.razor.cs +++ b/samples/SharedDemo/Demos/Performance.razor.cs @@ -7,7 +7,7 @@ namespace SharedDemo.Demos { public class PerformanceCompoent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { @@ -23,8 +23,8 @@ protected override void OnInitialized() var sourcePort = node1.AddPort(PortAlignment.Right); var targetPort = node2.AddPort(PortAlignment.Left); - diagramManager.Nodes.Add(node1, node2); - diagramManager.Links.Add(new LinkModel(sourcePort, targetPort)); + diagram.Nodes.Add(node1, node2); + diagram.Links.Add(new LinkModel(sourcePort, targetPort)); } } } diff --git a/samples/SharedDemo/Demos/Simple.razor b/samples/SharedDemo/Demos/Simple.razor index fb56f764..26cfc86b 100644 --- a/samples/SharedDemo/Demos/Simple.razor +++ b/samples/SharedDemo/Demos/Simple.razor @@ -29,7 +29,7 @@ - + diff --git a/samples/SharedDemo/Demos/SnapToGrid.razor b/samples/SharedDemo/Demos/SnapToGrid.razor index e2af2ff7..e1c2de7a 100644 --- a/samples/SharedDemo/Demos/SnapToGrid.razor +++ b/samples/SharedDemo/Demos/SnapToGrid.razor @@ -15,7 +15,7 @@ } } - + diff --git a/samples/SharedDemo/Demos/SnapToGrid.razor.cs b/samples/SharedDemo/Demos/SnapToGrid.razor.cs index ae246420..beaada30 100644 --- a/samples/SharedDemo/Demos/SnapToGrid.razor.cs +++ b/samples/SharedDemo/Demos/SnapToGrid.razor.cs @@ -7,7 +7,7 @@ namespace SharedDemo { public class SnapToGridComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(new DiagramOptions + protected readonly Diagram diagram = new Diagram(new DiagramOptions { GridSize = 50 }); @@ -18,8 +18,8 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagram.Nodes.Add(node1, node2, NewNode(300, 50)); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/ZoomToFit.razor b/samples/SharedDemo/Demos/ZoomToFit.razor index fe084e43..341b9a5a 100644 --- a/samples/SharedDemo/Demos/ZoomToFit.razor +++ b/samples/SharedDemo/Demos/ZoomToFit.razor @@ -17,8 +17,8 @@ + @onclick="() => diagram.ZoomToFit(50)">Zoom to fit - + diff --git a/samples/SharedDemo/Demos/ZoomToFit.razor.cs b/samples/SharedDemo/Demos/ZoomToFit.razor.cs index f371f521..1fa2ad2a 100644 --- a/samples/SharedDemo/Demos/ZoomToFit.razor.cs +++ b/samples/SharedDemo/Demos/ZoomToFit.razor.cs @@ -7,7 +7,7 @@ namespace SharedDemo.Demos { public class ZoomToFitComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { @@ -23,8 +23,8 @@ protected override void OnInitialized() var sourcePort = node1.AddPort(PortAlignment.Right); var targetPort = node2.AddPort(PortAlignment.Left); - diagramManager.Nodes.Add(node1, node2); - diagramManager.Links.Add(new LinkModel(sourcePort, targetPort)); + diagram.Nodes.Add(node1, node2); + diagram.Links.Add(new LinkModel(sourcePort, targetPort)); } } } diff --git a/samples/SharedDemo/Options.razor b/samples/SharedDemo/Options.razor index 1faa667e..07fc4a29 100644 --- a/samples/SharedDemo/Options.razor +++ b/samples/SharedDemo/Options.razor @@ -77,7 +77,7 @@ DefaultSelectedColor = "red" } }; - DiagramManager = new DiagramManager(options); + Diagram = new Diagram(options); }
diff --git a/samples/SharedDemo/QuickStart.razor b/samples/SharedDemo/QuickStart.razor index 52ed8c7e..db28e765 100644 --- a/samples/SharedDemo/QuickStart.razor +++ b/samples/SharedDemo/QuickStart.razor @@ -69,22 +69,22 @@

Creating a diagram manager

- Every diagram has a DiagramManager, go ahead and create one in the component + Every diagram has a Diagram, go ahead and create one in the component where you want to show it:

-
private DiagramManager DiagramManager { get; private set; }
+                    
private Diagram Diagram { get; private set; }
 
 protected override void OnInitialized()
 {
     base.OnInitialized();
 
-    DiagramManager = new DiagramManager();
+    Diagram = new Diagram();
 }

Changing options

- Every DiagramManager has a list of options that you can change: + Every Diagram has a list of options that you can change:

protected override void OnInitialized()
 {
@@ -101,7 +101,7 @@ protected override void OnInitialized()
             // Options related to links
         }
     };
-    DiagramManager = new DiagramManager(options);
+    Diagram = new Diagram(options);
 }
@@ -118,7 +118,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(300, 50); - DiagramManager.Nodes.Add(node1, node2, node3); + Diagram.Nodes.Add(node1, node2, node3); } private NodeModel NewNode(double x, double y) @@ -143,8 +143,8 @@ private NodeModel NewNode(double x, double y) var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); var node3 = NewNode(300, 50); - DiagramManager.Nodes.Add(node1, node2, node3); - DiagramManager.AddLink(node1.GetPort(PortAlignment.RIGHT), node2.GetPort(PortAlignment.LEFT)); + Diagram.Nodes.Add(node1, node2, node3); + Diagram.AddLink(node1.GetPort(PortAlignment.RIGHT), node2.GetPort(PortAlignment.LEFT)); }

The AddLink method adds a link of type LinkModel, which is the default @@ -167,7 +167,7 @@ private NodeModel NewNode(double x, double y)

Adding the canvas

<!-- Add this where you want to show the diagram -->
-<CascadingValue Name="DiagramManager" Value="DiagramManager">
+<CascadingValue Name="Diagram" Value="Diagram">
     <DiagramCanvas></DiagramCanvas>
 </CascadingValue>
diff --git a/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs b/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs index 5cad7986..3c936435 100644 --- a/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs +++ b/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs @@ -8,12 +8,12 @@ namespace Blazor.Diagrams.Algorithms { public static class LinksReconnectionAlgorithms { - public static void ReconnectLinksToClosestPorts(this DiagramManager diagramManager) + public static void ReconnectLinksToClosestPorts(this Diagram diagram) { // Only refresh ports once var portsToRefresh = new HashSet(); - foreach (var link in diagramManager.Links.ToArray()) + foreach (var link in diagram.Links.ToArray()) { if (link.TargetPort == null) continue; diff --git a/src/Blazor.Diagrams.Core/Behavior.cs b/src/Blazor.Diagrams.Core/Behavior.cs index fd103a02..2d81088f 100644 --- a/src/Blazor.Diagrams.Core/Behavior.cs +++ b/src/Blazor.Diagrams.Core/Behavior.cs @@ -4,12 +4,12 @@ namespace Blazor.Diagrams.Core { public abstract class Behavior : IDisposable { - public Behavior(DiagramManager diagramManager) + public Behavior(Diagram diagram) { - DiagramManager = diagramManager; + Diagram = diagram; } - protected DiagramManager DiagramManager { get; } + protected Diagram Diagram { get; } public abstract void Dispose(); } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs index 1d2376b7..9e3bee89 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs @@ -7,33 +7,33 @@ namespace Blazor.Diagrams.Core.Behaviors { public class DebugEventsBehavior : Behavior { - public DebugEventsBehavior(DiagramManager diagramManager) : base(diagramManager) + public DebugEventsBehavior(Diagram diagram) : base(diagram) { - DiagramManager.Changed += DiagramManager_Changed; - DiagramManager.ContainerChanged += DiagramManager_ContainerChanged; - DiagramManager.PanChanged += DiagramManager_PanChanged; - DiagramManager.Nodes.Added += Nodes_Added; - DiagramManager.Nodes.Removed += Nodes_Removed; - DiagramManager.Links.Added += Links_Added; - DiagramManager.Links.Removed += Links_Removed; - DiagramManager.GroupAdded += DiagramManager_GroupAdded; - DiagramManager.GroupRemoved += DiagramManager_GroupRemoved; - DiagramManager.GroupUngrouped += DiagramManager_GroupUngrouped; - DiagramManager.SelectionChanged += DiagramManager_SelectionChanged; - DiagramManager.ZoomChanged += DiagramManager_ZoomChanged; + Diagram.Changed += Diagram_Changed; + Diagram.ContainerChanged += Diagram_ContainerChanged; + Diagram.PanChanged += Diagram_PanChanged; + Diagram.Nodes.Added += Nodes_Added; + Diagram.Nodes.Removed += Nodes_Removed; + Diagram.Links.Added += Links_Added; + Diagram.Links.Removed += Links_Removed; + Diagram.GroupAdded += Diagram_GroupAdded; + Diagram.GroupRemoved += Diagram_GroupRemoved; + Diagram.GroupUngrouped += Diagram_GroupUngrouped; + Diagram.SelectionChanged += Diagram_SelectionChanged; + Diagram.ZoomChanged += Diagram_ZoomChanged; } - private void DiagramManager_ZoomChanged() + private void Diagram_ZoomChanged() { - Console.WriteLine($"ZoomChanged, Zoom={DiagramManager.Zoom}"); + Console.WriteLine($"ZoomChanged, Zoom={Diagram.Zoom}"); } - private void DiagramManager_SelectionChanged(SelectableModel obj) + private void Diagram_SelectionChanged(SelectableModel obj) { Console.WriteLine($"SelectionChanged, Model={obj.GetType().Name}, Selected={obj.Selected}"); } - private void DiagramManager_GroupUngrouped(GroupModel obj) + private void Diagram_GroupUngrouped(GroupModel obj) { Console.WriteLine($"GroupUngrouped, Id={obj.Id}"); } @@ -58,45 +58,45 @@ private void Nodes_Added(NodeModel[] obj) Console.WriteLine($"Nodes.Added, Nodes=[{string.Join(", ", obj.Select(x => x.Id))}]"); } - private void DiagramManager_PanChanged() + private void Diagram_PanChanged() { - Console.WriteLine($"PanChanged, Pan={DiagramManager.Pan}"); + Console.WriteLine($"PanChanged, Pan={Diagram.Pan}"); } - private void DiagramManager_GroupRemoved(GroupModel obj) + private void Diagram_GroupRemoved(GroupModel obj) { Console.WriteLine($"GroupRemoved, Id={obj.Id}"); } - private void DiagramManager_GroupAdded(GroupModel obj) + private void Diagram_GroupAdded(GroupModel obj) { Console.WriteLine($"GroupAdded, Id={obj.Id}"); } - private void DiagramManager_ContainerChanged() + private void Diagram_ContainerChanged() { - Console.WriteLine($"ContainerChanged, Container={DiagramManager.Container}"); + Console.WriteLine($"ContainerChanged, Container={Diagram.Container}"); } - private void DiagramManager_Changed() + private void Diagram_Changed() { Console.WriteLine("Changed"); } public override void Dispose() { - DiagramManager.Changed -= DiagramManager_Changed; - DiagramManager.ContainerChanged -= DiagramManager_ContainerChanged; - DiagramManager.PanChanged -= DiagramManager_PanChanged; - DiagramManager.Nodes.Added -= Nodes_Added; - DiagramManager.Nodes.Removed -= Nodes_Removed; - DiagramManager.Links.Added -= Links_Added; - DiagramManager.Links.Removed -= Links_Removed; - DiagramManager.GroupAdded -= DiagramManager_GroupAdded; - DiagramManager.GroupRemoved -= DiagramManager_GroupRemoved; - DiagramManager.GroupUngrouped -= DiagramManager_GroupUngrouped; - DiagramManager.SelectionChanged -= DiagramManager_SelectionChanged; - DiagramManager.ZoomChanged -= DiagramManager_ZoomChanged; + Diagram.Changed -= Diagram_Changed; + Diagram.ContainerChanged -= Diagram_ContainerChanged; + Diagram.PanChanged -= Diagram_PanChanged; + Diagram.Nodes.Added -= Nodes_Added; + Diagram.Nodes.Removed -= Nodes_Removed; + Diagram.Links.Added -= Links_Added; + Diagram.Links.Removed -= Links_Removed; + Diagram.GroupAdded -= Diagram_GroupAdded; + Diagram.GroupRemoved -= Diagram_GroupRemoved; + Diagram.GroupUngrouped -= Diagram_GroupUngrouped; + Diagram.SelectionChanged -= Diagram_SelectionChanged; + Diagram.ZoomChanged -= Diagram_ZoomChanged; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs index 6435eb0d..fe0e0a2f 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DeleteSelectionBehavior.cs @@ -7,40 +7,40 @@ namespace Blazor.Diagrams.Core.Behaviors { public class DeleteSelectionBehavior : Behavior { - public DeleteSelectionBehavior(DiagramManager diagramManager) : base(diagramManager) + public DeleteSelectionBehavior(Diagram diagram) : base(diagram) { - DiagramManager.KeyDown += DiagramManager_KeyDown; + Diagram.KeyDown += Diagram_KeyDown; } - private void DiagramManager_KeyDown(KeyboardEventArgs e) + private void Diagram_KeyDown(KeyboardEventArgs e) { - if (e.AltKey || e.CtrlKey || e.ShiftKey || e.Code != DiagramManager.Options.DeleteKey) + if (e.AltKey || e.CtrlKey || e.ShiftKey || e.Code != Diagram.Options.DeleteKey) return; // TODO: BATCH REFRESH - foreach (var sm in DiagramManager.GetSelectedModels().ToList()) + foreach (var sm in Diagram.GetSelectedModels().ToList()) { if (sm.Locked) continue; if (sm is GroupModel group) { - DiagramManager.RemoveGroup(group); + Diagram.RemoveGroup(group); } else if (sm is NodeModel node) { - DiagramManager.Nodes.Remove(node); + Diagram.Nodes.Remove(node); } else if (sm is BaseLinkModel link) { - DiagramManager.Links.Remove(link); + Diagram.Links.Remove(link); } } } public override void Dispose() { - DiagramManager.KeyDown -= DiagramManager_KeyDown; + Diagram.KeyDown -= Diagram_KeyDown; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index da35a5d5..f46fce51 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -12,20 +12,20 @@ public class DragMovablesBehavior : Behavior private double? _lastClientX; private double? _lastClientY; - public DragMovablesBehavior(DiagramManager diagramManager) : base(diagramManager) + public DragMovablesBehavior(Diagram diagram) : base(diagram) { - DiagramManager.MouseDown += DiagramManager_MouseDown; - DiagramManager.MouseMove += DiagramManager_MouseMove; - DiagramManager.MouseUp += DiagramManager_MouseUp; + Diagram.MouseDown += Diagram_MouseDown; + Diagram.MouseMove += Diagram_MouseMove; + Diagram.MouseUp += Diagram_MouseUp; } - private void DiagramManager_MouseDown(Model model, MouseEventArgs e) + private void Diagram_MouseDown(Model model, MouseEventArgs e) { if (!(model is MovableModel)) return; // Don't like this linq - _initialPositions = DiagramManager.GetSelectedModels() + _initialPositions = Diagram.GetSelectedModels() .Where(m => m is MovableModel) .Select(m => (m as MovableModel)!.Position) .ToArray(); @@ -34,16 +34,16 @@ private void DiagramManager_MouseDown(Model model, MouseEventArgs e) _lastClientY = e.ClientY; } - private void DiagramManager_MouseMove(Model model, MouseEventArgs e) + private void Diagram_MouseMove(Model model, MouseEventArgs e) { if (_initialPositions == null || _lastClientX == null || _lastClientY == null) return; - var deltaX = (e.ClientX - _lastClientX.Value) / DiagramManager.Zoom; - var deltaY = (e.ClientY - _lastClientY.Value) / DiagramManager.Zoom; + var deltaX = (e.ClientX - _lastClientX.Value) / Diagram.Zoom; + var deltaY = (e.ClientY - _lastClientY.Value) / Diagram.Zoom; var i = 0; - foreach (var sm in DiagramManager.GetSelectedModels()) + foreach (var sm in Diagram.GetSelectedModels()) { if (!(sm is MovableModel node) || node.Locked) continue; @@ -58,10 +58,10 @@ private void DiagramManager_MouseMove(Model model, MouseEventArgs e) private double ApplyGridSize(double n) { - if (DiagramManager.Options.GridSize == null) + if (Diagram.Options.GridSize == null) return n; - var gridSize = DiagramManager.Options.GridSize.Value; + var gridSize = Diagram.Options.GridSize.Value; // 20 * floor((100 + 10) / 20) = 20 * 5 = 100 // 20 * floor((105 + 10) / 20) = 20 * 5 = 100 @@ -69,7 +69,7 @@ private double ApplyGridSize(double n) return gridSize * Math.Floor((n + gridSize / 2) / gridSize); } - private void DiagramManager_MouseUp(Model model, MouseEventArgs e) + private void Diagram_MouseUp(Model model, MouseEventArgs e) { _initialPositions = null; _lastClientX = null; @@ -78,9 +78,9 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) public override void Dispose() { - DiagramManager.MouseDown -= DiagramManager_MouseDown; - DiagramManager.MouseMove -= DiagramManager_MouseMove; - DiagramManager.MouseUp -= DiagramManager_MouseUp; + Diagram.MouseDown -= Diagram_MouseDown; + Diagram.MouseMove -= Diagram_MouseMove; + Diagram.MouseUp -= Diagram_MouseUp; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index cd77aa82..4a8a6614 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -13,39 +13,39 @@ public class DragNewLinkBehavior : Behavior private double _initialY; private BaseLinkModel? _ongoingLink; - public DragNewLinkBehavior(DiagramManager diagramManager) : base(diagramManager) + public DragNewLinkBehavior(Diagram diagram) : base(diagram) { - DiagramManager.MouseDown += DiagramManager_MouseDown; - DiagramManager.MouseMove += DiagramManager_MouseMove; - DiagramManager.MouseUp += DiagramManager_MouseUp; + Diagram.MouseDown += Diagram_MouseDown; + Diagram.MouseMove += Diagram_MouseMove; + Diagram.MouseUp += Diagram_MouseUp; } - private void DiagramManager_MouseDown(Model model, MouseEventArgs e) + private void Diagram_MouseDown(Model model, MouseEventArgs e) { if (!(model is PortModel port) || port.Locked || e.Button != (int)MouseEventButton.Left) return; _initialX = e.ClientX; _initialY = e.ClientY; - _ongoingLink = DiagramManager.Options.Links.Factory(DiagramManager, port); + _ongoingLink = Diagram.Options.Links.Factory(Diagram, port); _ongoingLink.OnGoingPosition = new Point(port.Position.X + port.Size.Width / 2, port.Position.Y + port.Size.Height / 2); - DiagramManager.Links.Add(_ongoingLink); + Diagram.Links.Add(_ongoingLink); } - private void DiagramManager_MouseMove(Model model, MouseEventArgs e) + private void Diagram_MouseMove(Model model, MouseEventArgs e) { if (_ongoingLink == null || model != null) return; - var deltaX = (e.ClientX - _initialX) / DiagramManager.Zoom; - var deltaY = (e.ClientY - _initialY) / DiagramManager.Zoom; + var deltaX = (e.ClientX - _initialX) / Diagram.Zoom; + var deltaY = (e.ClientY - _initialY) / Diagram.Zoom; var sX = _ongoingLink.SourcePort.Position.X + _ongoingLink.SourcePort.Size.Width / 2; var sY = _ongoingLink.SourcePort.Position.Y + _ongoingLink.SourcePort.Size.Height / 2; _ongoingLink.OnGoingPosition = new Point(sX + deltaX, sY + deltaY); - if (DiagramManager.Options.Links.EnableSnapping) + if (Diagram.Options.Links.EnableSnapping) { var nearPort = FindNearPortToAttachTo(); if (nearPort != null || _ongoingLink.TargetPort != null) @@ -60,7 +60,7 @@ private void DiagramManager_MouseMove(Model model, MouseEventArgs e) _ongoingLink.Refresh(); } - private void DiagramManager_MouseUp(Model model, MouseEventArgs e) + private void Diagram_MouseUp(Model model, MouseEventArgs e) { if (_ongoingLink == null) return; @@ -73,7 +73,7 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) if (!(model is PortModel port) || !_ongoingLink.SourcePort.CanAttachTo(port)) { - DiagramManager.Links.Remove(_ongoingLink); + Diagram.Links.Remove(_ongoingLink); _ongoingLink = null; return; } @@ -88,9 +88,9 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) private PortModel? FindNearPortToAttachTo() { - foreach (var port in DiagramManager.Nodes.SelectMany(n => n.Ports)) + foreach (var port in Diagram.Nodes.SelectMany(n => n.Ports)) { - if (_ongoingLink!.OnGoingPosition!.DistanceTo(port.Position) < DiagramManager.Options.Links.SnappingRadius && + if (_ongoingLink!.OnGoingPosition!.DistanceTo(port.Position) < Diagram.Options.Links.SnappingRadius && _ongoingLink.SourcePort.CanAttachTo(port)) return port; } @@ -100,9 +100,9 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) public override void Dispose() { - DiagramManager.MouseDown -= DiagramManager_MouseDown; - DiagramManager.MouseMove -= DiagramManager_MouseMove; - DiagramManager.MouseUp -= DiagramManager_MouseUp; + Diagram.MouseDown -= Diagram_MouseDown; + Diagram.MouseMove -= Diagram_MouseMove; + Diagram.MouseUp -= Diagram_MouseUp; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs index 349c1db8..ba6fe5fe 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs @@ -9,11 +9,11 @@ public class EventsBehavior : Behavior private bool _captureMouseMove; private int _mouseMovedCount; - public EventsBehavior(DiagramManager diagramManager) : base(diagramManager) + public EventsBehavior(Diagram diagram) : base(diagram) { - DiagramManager.MouseDown += OnMouseDown; - DiagramManager.MouseMove += OnMouseMove; - DiagramManager.MouseUp += OnMouseUp; + Diagram.MouseDown += OnMouseDown; + Diagram.MouseMove += OnMouseMove; + Diagram.MouseUp += OnMouseUp; } private void OnMouseDown(Model model, MouseEventArgs e) @@ -38,14 +38,14 @@ private void OnMouseUp(Model model, MouseEventArgs e) return; } - DiagramManager.OnMouseClick(model, e); + Diagram.OnMouseClick(model, e); } public override void Dispose() { - DiagramManager.MouseDown -= OnMouseDown; - DiagramManager.MouseMove -= OnMouseMove; - DiagramManager.MouseUp -= OnMouseUp; + Diagram.MouseDown -= OnMouseDown; + Diagram.MouseMove -= OnMouseMove; + Diagram.MouseUp -= OnMouseUp; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs index f28f9237..236d85af 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs @@ -6,31 +6,31 @@ namespace Blazor.Diagrams.Core.Behaviors { public class GroupingBehavior : Behavior { - public GroupingBehavior(DiagramManager diagramManager) : base(diagramManager) + public GroupingBehavior(Diagram diagram) : base(diagram) { - DiagramManager.KeyDown += DiagramManager_KeyDown; + Diagram.KeyDown += Diagram_KeyDown; } - private void DiagramManager_KeyDown(KeyboardEventArgs e) + private void Diagram_KeyDown(KeyboardEventArgs e) { - if (!DiagramManager.Options.Groups.Enabled) + if (!Diagram.Options.Groups.Enabled) return; - if (!DiagramManager.GetSelectedModels().Any()) + if (!Diagram.GetSelectedModels().Any()) return; - Console.WriteLine(DiagramManager.Options.Groups.KeyboardShortcut(e)); - if (!DiagramManager.Options.Groups.KeyboardShortcut(e)) + Console.WriteLine(Diagram.Options.Groups.KeyboardShortcut(e)); + if (!Diagram.Options.Groups.KeyboardShortcut(e)) return; - var selectedNodes = DiagramManager.Nodes.Where(n => n.Selected).ToArray(); + var selectedNodes = Diagram.Nodes.Where(n => n.Selected).ToArray(); var nodesWithGroup = selectedNodes.Where(n => n.Group != null).ToArray(); if (nodesWithGroup.Length > 0) { // Ungroup foreach (var group in nodesWithGroup.GroupBy(n => n.Group!).Select(g => g.Key)) { - DiagramManager.Ungroup(group); + Diagram.Ungroup(group); } } else @@ -45,13 +45,13 @@ private void DiagramManager_KeyDown(KeyboardEventArgs e) if (selectedNodes.Select(n => n.Layer).Distinct().Count() > 1) return; - DiagramManager.Group(selectedNodes); + Diagram.Group(selectedNodes); } } public override void Dispose() { - DiagramManager.KeyDown -= DiagramManager_KeyDown; + Diagram.KeyDown -= Diagram_KeyDown; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs index 46b5d060..880a304d 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs @@ -11,36 +11,36 @@ public class PanBehavior : Behavior private double _lastClientX; private double _lastClientY; - public PanBehavior(DiagramManager diagramManager) : base(diagramManager) + public PanBehavior(Diagram diagram) : base(diagram) { - DiagramManager.MouseDown += DiagramManager_MouseDown; - DiagramManager.MouseMove += DiagramManager_MouseMove; - DiagramManager.MouseUp += DiagramManager_MouseUp; + Diagram.MouseDown += Diagram_MouseDown; + Diagram.MouseMove += Diagram_MouseMove; + Diagram.MouseUp += Diagram_MouseUp; } - private void DiagramManager_MouseDown(Model model, MouseEventArgs e) + private void Diagram_MouseDown(Model model, MouseEventArgs e) { - if (!DiagramManager.Options.AllowPanning || model != null || e.ShiftKey) + if (!Diagram.Options.AllowPanning || model != null || e.ShiftKey) return; - _initialPan = DiagramManager.Pan; + _initialPan = Diagram.Pan; _lastClientX = e.ClientX; _lastClientY = e.ClientY; } - private void DiagramManager_MouseMove(Model model, MouseEventArgs e) + private void Diagram_MouseMove(Model model, MouseEventArgs e) { - if (!DiagramManager.Options.AllowPanning || _initialPan == null) + if (!Diagram.Options.AllowPanning || _initialPan == null) return; - var deltaX = e.ClientX - _lastClientX - (DiagramManager.Pan.X - _initialPan.X); - var deltaY = e.ClientY - _lastClientY - (DiagramManager.Pan.Y - _initialPan.Y); - DiagramManager.UpdatePan(deltaX, deltaY); + var deltaX = e.ClientX - _lastClientX - (Diagram.Pan.X - _initialPan.X); + var deltaY = e.ClientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y); + Diagram.UpdatePan(deltaX, deltaY); } - private void DiagramManager_MouseUp(Model model, MouseEventArgs e) + private void Diagram_MouseUp(Model model, MouseEventArgs e) { - if (!DiagramManager.Options.AllowPanning) + if (!Diagram.Options.AllowPanning) return; _initialPan = null; @@ -48,9 +48,9 @@ private void DiagramManager_MouseUp(Model model, MouseEventArgs e) public override void Dispose() { - DiagramManager.MouseDown -= DiagramManager_MouseDown; - DiagramManager.MouseMove -= DiagramManager_MouseMove; - DiagramManager.MouseUp -= DiagramManager_MouseUp; + Diagram.MouseDown -= Diagram_MouseDown; + Diagram.MouseMove -= Diagram_MouseMove; + Diagram.MouseUp -= Diagram_MouseUp; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs index 4446a42d..13c54eda 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs @@ -5,33 +5,33 @@ namespace Blazor.Diagrams.Core.Behaviors { public class SelectionBehavior : Behavior { - public SelectionBehavior(DiagramManager diagramManager) : base(diagramManager) + public SelectionBehavior(Diagram diagram) : base(diagram) { - DiagramManager.MouseDown += DiagramManager_MouseDown; + Diagram.MouseDown += Diagram_MouseDown; } - private void DiagramManager_MouseDown(Model model, MouseEventArgs e) + private void Diagram_MouseDown(Model model, MouseEventArgs e) { if (model == null) { - DiagramManager.UnselectAll(); + Diagram.UnselectAll(); } else if (model is SelectableModel sm) { if (e.CtrlKey && sm.Selected) { - DiagramManager.UnselectModel(sm); + Diagram.UnselectModel(sm); } else if (!sm.Selected) { - DiagramManager.SelectModel(sm, !e.CtrlKey || !DiagramManager.Options.AllowMultiSelection); + Diagram.SelectModel(sm, !e.CtrlKey || !Diagram.Options.AllowMultiSelection); } } } public override void Dispose() { - DiagramManager.MouseDown -= DiagramManager_MouseDown; + Diagram.MouseDown -= Diagram_MouseDown; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs index 2d5bb796..bf576282 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs @@ -8,18 +8,18 @@ public class ZoomBehavior : Behavior { private const float _scaleBy = 1.05f; - public ZoomBehavior(DiagramManager diagramManager) : base(diagramManager) + public ZoomBehavior(Diagram diagram) : base(diagram) { - DiagramManager.Wheel += DiagramManager_Wheel; + Diagram.Wheel += Diagram_Wheel; } - private void DiagramManager_Wheel(WheelEventArgs e) + private void Diagram_Wheel(WheelEventArgs e) { - if (!DiagramManager.Options.Zoom.Enabled) + if (!Diagram.Options.Zoom.Enabled) return; - var oldZoom = DiagramManager.Zoom; - var deltaY = DiagramManager.Options.Zoom.Inverse ? e.DeltaY * -1 : e.DeltaY; + var oldZoom = Diagram.Zoom; + var deltaY = Diagram.Options.Zoom.Inverse ? e.DeltaY * -1 : e.DeltaY; var newZoom = deltaY > 0 ? oldZoom * _scaleBy : oldZoom / _scaleBy; if (newZoom < 0) @@ -27,28 +27,28 @@ private void DiagramManager_Wheel(WheelEventArgs e) // Other algorithms (based only on the changes in the zoom) don't work for our case // This solution is taken as is from react-diagrams (ZoomCanvasAction) - var clientWidth = DiagramManager.Container.Width; - var clientHeight = DiagramManager.Container.Height; + var clientWidth = Diagram.Container.Width; + var clientHeight = Diagram.Container.Height; var widthDiff = clientWidth * newZoom - clientWidth * oldZoom; var heightDiff = clientHeight * newZoom - clientHeight * oldZoom; - var clientX = e.ClientX - DiagramManager.Container.Left; - var clientY = e.ClientY - DiagramManager.Container.Top; - var xFactor = (clientX - DiagramManager.Pan.X) / oldZoom / clientWidth; - var yFactor = (clientY - DiagramManager.Pan.Y) / oldZoom / clientHeight; - var newPanX = DiagramManager.Pan.X - widthDiff * xFactor; - var newPanY = DiagramManager.Pan.Y - heightDiff * yFactor; - - newZoom = Math.Clamp(newZoom, DiagramManager.Options.Zoom.Minimum, DiagramManager.Options.Zoom.Maximum); - if (newZoom == DiagramManager.Zoom) + var clientX = e.ClientX - Diagram.Container.Left; + var clientY = e.ClientY - Diagram.Container.Top; + var xFactor = (clientX - Diagram.Pan.X) / oldZoom / clientWidth; + var yFactor = (clientY - Diagram.Pan.Y) / oldZoom / clientHeight; + var newPanX = Diagram.Pan.X - widthDiff * xFactor; + var newPanY = Diagram.Pan.Y - heightDiff * yFactor; + + newZoom = Math.Clamp(newZoom, Diagram.Options.Zoom.Minimum, Diagram.Options.Zoom.Maximum); + if (newZoom == Diagram.Zoom) return; - DiagramManager.Pan = new Point(newPanX, newPanY); - DiagramManager.SetZoom(newZoom); + Diagram.Pan = new Point(newPanX, newPanY); + Diagram.SetZoom(newZoom); } public override void Dispose() { - DiagramManager.Wheel -= DiagramManager_Wheel; + Diagram.Wheel -= Diagram_Wheel; } } } diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs index e0836b1b..f6340085 100644 --- a/src/Blazor.Diagrams.Core/Delegates.cs +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -4,11 +4,11 @@ namespace Blazor.Diagrams.Core { - public delegate Point[] Router(DiagramManager diagram, BaseLinkModel link, Point from, Point to); + public delegate Point[] Router(Diagram diagram, BaseLinkModel link, Point from, Point to); - public delegate PathGeneratorResult PathGenerator(DiagramManager diagram, BaseLinkModel link, Point[] route); + public delegate PathGeneratorResult PathGenerator(Diagram diagram, BaseLinkModel link, Point[] route); - public delegate BaseLinkModel LinkFactory(DiagramManager diagram, PortModel sourcePort); + public delegate BaseLinkModel LinkFactory(Diagram diagram, PortModel sourcePort); - public delegate GroupModel GroupFactory(DiagramManager diagram, NodeModel[] children); + public delegate GroupModel GroupFactory(Diagram diagram, NodeModel[] children); } diff --git a/src/Blazor.Diagrams.Core/DiagramManager.cs b/src/Blazor.Diagrams.Core/Diagram.cs similarity index 99% rename from src/Blazor.Diagrams.Core/DiagramManager.cs rename to src/Blazor.Diagrams.Core/Diagram.cs index 6af59fad..efb5be84 100644 --- a/src/Blazor.Diagrams.Core/DiagramManager.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -13,7 +13,7 @@ [assembly: InternalsVisibleTo("Blazor.Diagrams")] namespace Blazor.Diagrams.Core { - public class DiagramManager + public class Diagram { private readonly Dictionary _behaviors; private readonly Dictionary _componentByModelMapping; @@ -36,7 +36,7 @@ public class DiagramManager public event Action? ZoomChanged; public event Action? ContainerChanged; - public DiagramManager(DiagramOptions? options = null) + public Diagram(DiagramOptions? options = null) { _behaviors = new Dictionary(); _componentByModelMapping = new Dictionary(); diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs index 1cca62c0..b8450680 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -10,7 +10,7 @@ public static partial class PathGenerators { private const double _margin = 125; - public static PathGeneratorResult Smooth(DiagramManager _, BaseLinkModel link, Point[] route) + public static PathGeneratorResult Smooth(Diagram _, BaseLinkModel link, Point[] route) { if (route.Length > 2) return CurveThroughPoints(route, link); diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs index 6f03843d..ec8639f0 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs @@ -6,7 +6,7 @@ namespace Blazor.Diagrams.Core { public static partial class PathGenerators { - public static PathGeneratorResult Straight(DiagramManager _, BaseLinkModel link, Point[] route) + public static PathGeneratorResult Straight(Diagram _, BaseLinkModel link, Point[] route) { route = (Point[])route.Clone(); double? sourceAngle = null; diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs index f9353dcf..fba82035 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs @@ -7,7 +7,7 @@ namespace Blazor.Diagrams.Core { public static partial class Routers { - public static Point[] Normal(DiagramManager _, BaseLinkModel link, Point from, Point to) + public static Point[] Normal(Diagram _, BaseLinkModel link, Point from, Point to) { var route = new Point[link.Vertices.Count + 2]; route[0] = from; diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index 976715a1..1bd3a6b5 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -11,12 +11,12 @@ @* Links *@ - @foreach (var link in DiagramManager.Links) + @foreach (var link in Diagram.Links) { } - @foreach (var node in DiagramManager.Nodes.Where(n => n.Layer == RenderLayer.SVG)) + @foreach (var node in Diagram.Nodes.Where(n => n.Layer == RenderLayer.SVG)) { } @@ -24,12 +24,12 @@ @* Nodes *@
- @foreach (var group in DiagramManager.Groups) + @foreach (var group in Diagram.Groups) { } - @foreach (var node in DiagramManager.Nodes.Where(n => n.Layer == RenderLayer.HTML && n.Group == null)) + @foreach (var node in Diagram.Nodes.Where(n => n.Layer == RenderLayer.HTML && n.Group == null)) { } diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs index 7a254674..11d40c12 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs @@ -11,8 +11,8 @@ namespace Blazor.Diagrams.Components { public partial class DiagramCanvas : IDisposable { - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Parameter] public RenderFragment Widgets { get; set; } @@ -28,14 +28,14 @@ public partial class DiagramCanvas : IDisposable private bool _shouldReRender; private string LayerStyle - => FormattableString.Invariant($"transform: translate({DiagramManager.Pan.X}px, {DiagramManager.Pan.Y}px) scale({DiagramManager.Zoom});"); + => FormattableString.Invariant($"transform: translate({Diagram.Pan.X}px, {Diagram.Pan.Y}px) scale({Diagram.Zoom});"); protected override void OnInitialized() { base.OnInitialized(); _reference = DotNetObjectReference.Create(this); - DiagramManager.Changed += OnDiagramChanged; + Diagram.Changed += OnDiagramChanged; } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -44,13 +44,13 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender) { - DiagramManager.Container = await JSRuntime.GetBoundingClientRect(elementReference); + Diagram.Container = await JSRuntime.GetBoundingClientRect(elementReference); await JSRuntime.ObserveResizes(elementReference, _reference, isCanvas: true); } } [JSInvokable] - public void OnResize(Rectangle rect) => DiagramManager.SetContainer(rect); + public void OnResize(Rectangle rect) => Diagram.SetContainer(rect); protected override bool ShouldRender() { @@ -63,15 +63,15 @@ protected override bool ShouldRender() return false; } - protected void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(null, e); + protected void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(null, e); - protected void OnMouseMove(MouseEventArgs e) => DiagramManager.OnMouseMove(null, e); + protected void OnMouseMove(MouseEventArgs e) => Diagram.OnMouseMove(null, e); - protected void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(null, e); + protected void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(null, e); - protected void OnKeyDown(KeyboardEventArgs e) => DiagramManager.OnKeyDown(e); + protected void OnKeyDown(KeyboardEventArgs e) => Diagram.OnKeyDown(e); - protected void OnWheel(WheelEventArgs e) => DiagramManager.OnWheel(e); + protected void OnWheel(WheelEventArgs e) => Diagram.OnWheel(e); private void OnDiagramChanged() { @@ -81,7 +81,7 @@ private void OnDiagramChanged() public void Dispose() { - DiagramManager.Changed -= OnDiagramChanged; + Diagram.Changed -= OnDiagramChanged; if (_reference == null) return; diff --git a/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs b/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs index 6b1609a1..63c20053 100644 --- a/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs +++ b/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs @@ -21,8 +21,8 @@ public partial class GroupContainer : IDisposable [Parameter] public RenderFragment ChildContent { get; set; } - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } public void Dispose() { @@ -66,8 +66,8 @@ private void OnGroupChanged() StateHasChanged(); } - private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Group, e); + private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Group, e); - private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Group, e); + private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Group, e); } } diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs index 4c4467ff..cd7fff8e 100644 --- a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs @@ -10,7 +10,7 @@ public partial class LinkVertexWidget : IDisposable { private bool _shouldRender = true; - [CascadingParameter(Name = "DiagramManager")] public DiagramManager DiagramManager { get; set; } + [CascadingParameter] public Diagram Diagram { get; set; } [Parameter] public LinkVertexModel Vertex { get; set; } [Parameter] public string Color { get; set; } [Parameter] public string SelectedColor { get; set; } @@ -44,9 +44,9 @@ private void OnVertexChanged() StateHasChanged(); } - private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Vertex, e); + private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Vertex, e); - private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Vertex, e); + private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Vertex, e); private void OnDoubleClick(MouseEventArgs e) { diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index f298d850..aacca108 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -1,13 +1,13 @@ @using SvgPathProperties; @{ - var router = Link.Router ?? DiagramManager.Options.Links.DefaultRouter; - var pathGenerator = Link.PathGenerator ?? DiagramManager.Options.Links.DefaultPathGenerator; + var router = Link.Router ?? Diagram.Options.Links.DefaultRouter; + var pathGenerator = Link.PathGenerator ?? Diagram.Options.Links.DefaultPathGenerator; var sourcePosition = GetPositionBasedOnAlignment(Link.SourcePort, Link.SourceMarker); var targetPosition = Link.IsAttached ? GetPositionBasedOnAlignment(Link.TargetPort, Link.TargetMarker) : Link.OnGoingPosition; - var route = router(DiagramManager, Link, sourcePosition, targetPosition); - var result = pathGenerator(DiagramManager, Link, route); - var color = Link.Selected ? (Link.SelectedColor ?? DiagramManager.Options.Links.DefaultSelectedColor) - : (Link.Color ?? DiagramManager.Options.Links.DefaultColor); + var route = router(Diagram, Link, sourcePosition, targetPosition); + var result = pathGenerator(Diagram, Link, route); + var color = Link.Selected ? (Link.SelectedColor ?? Diagram.Options.Links.DefaultSelectedColor) + : (Link.Color ?? Diagram.Options.Links.DefaultColor); var paths = Link.Labels.Count > 0 ? result.Paths.Select(p => new SVGPathProperties(p)).ToArray() : Array.Empty(); } @@ -49,8 +49,8 @@ @if (Link.Vertices.Count > 0) { - var selectedColor = Link.SelectedColor ?? DiagramManager.Options.Links.DefaultSelectedColor; - var normalColor = Link.Color ?? DiagramManager.Options.Links.DefaultColor; + var selectedColor = Link.SelectedColor ?? Diagram.Options.Links.DefaultSelectedColor; + var normalColor = Link.Color ?? Diagram.Options.Links.DefaultColor; @foreach (var vertex in Link.Vertices) { diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index f709d066..e86908a3 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -9,8 +9,8 @@ namespace Blazor.Diagrams.Components { public partial class LinkWidget { - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Parameter] public LinkModel Link { get; set; } @@ -47,10 +47,10 @@ private void OnMouseDown(MouseEventArgs e, int index) if (!Link.Segmentable) return; - var rPt = DiagramManager.GetRelativeMousePoint(e.ClientX, e.ClientY); + var rPt = Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY); var vertex = new LinkVertexModel(Link, rPt); Link.Vertices.Insert(index, vertex); - DiagramManager.OnMouseDown(vertex, e); + Diagram.OnMouseDown(vertex, e); } } } diff --git a/src/Blazor.Diagrams/Components/NavigatorWidget.razor b/src/Blazor.Diagrams/Components/NavigatorWidget.razor index 6872461e..57ff9f1b 100644 --- a/src/Blazor.Diagrams/Components/NavigatorWidget.razor +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor @@ -1,11 +1,11 @@ -@if (DiagramManager.Container != null) +@if (Diagram.Container != null) { - var addedNodeX = Math.Max(0, DiagramManager.Pan.X); - var addedNodeY = Math.Max(0, DiagramManager.Pan.Y); - var addedCurrentViewX = Math.Abs(Math.Min(0, DiagramManager.Pan.X)) * XFactor; - var addedCurrentViewY = Math.Abs(Math.Min(0, DiagramManager.Pan.Y)) * YFactor; - var currentViewWidth = DiagramManager.Container.Width * XFactor; - var currentViewHeight = DiagramManager.Container.Height * YFactor; + var addedNodeX = Math.Max(0, Diagram.Pan.X); + var addedNodeY = Math.Max(0, Diagram.Pan.Y); + var addedCurrentViewX = Math.Abs(Math.Min(0, Diagram.Pan.X)) * XFactor; + var addedCurrentViewY = Math.Abs(Math.Min(0, Diagram.Pan.Y)) * YFactor; + var currentViewWidth = Diagram.Container.Width * XFactor; + var currentViewHeight = Diagram.Container.Height * YFactor;
@@ -16,12 +16,12 @@ height: @(currentViewHeight.ToInvariantString())px;">
- @foreach (var node in DiagramManager.Nodes.Where(n => n.Size != null)) + @foreach (var node in Diagram.Nodes.Where(n => n.Size != null)) { - var left = (Math.Max(0, node.Position.X * DiagramManager.Zoom) + addedNodeX) * XFactor; - var top = (Math.Max(0, node.Position.Y * DiagramManager.Zoom) + addedNodeY) * YFactor; - var width = node.Size.Width * DiagramManager.Zoom * XFactor; - var height = node.Size.Height * DiagramManager.Zoom * YFactor; + var left = (Math.Max(0, node.Position.X * Diagram.Zoom) + addedNodeX) * XFactor; + var top = (Math.Max(0, node.Position.Y * Diagram.Zoom) + addedNodeY) * YFactor; + var width = node.Size.Width * Diagram.Zoom * XFactor; + var height = node.Size.Height * Diagram.Zoom * YFactor; } - @foreach (var group in DiagramManager.Groups.Where(g => !Size.Zero.Equals(g.Size))) + @foreach (var group in Diagram.Groups.Where(g => !Size.Zero.Equals(g.Size))) { - var left = (Math.Max(0, group.Position.X * DiagramManager.Zoom) + addedNodeX) * XFactor; - var top = (Math.Max(0, group.Position.Y * DiagramManager.Zoom) + addedNodeY) * YFactor; - var width = group.Size.Width * DiagramManager.Zoom * XFactor; - var height = group.Size.Height * DiagramManager.Zoom * YFactor; + var left = (Math.Max(0, group.Position.X * Diagram.Zoom) + addedNodeX) * XFactor; + var top = (Math.Max(0, group.Position.Y * Diagram.Zoom) + addedNodeY) * YFactor; + var width = group.Size.Width * Diagram.Zoom * XFactor; + var height = group.Size.Height * Diagram.Zoom * YFactor; Refresh(); + private void Diagram_Changed() => Refresh(); - private void DiagramManager_NodesAdded(NodeModel[] nodes) + private void Diagram_NodesAdded(NodeModel[] nodes) { foreach (var node in nodes) { @@ -53,7 +53,7 @@ private void DiagramManager_NodesAdded(NodeModel[] nodes) } } - private void DiagramManager_NodesRemoved(NodeModel[] nodes) + private void Diagram_NodesRemoved(NodeModel[] nodes) { foreach (var node in nodes) { @@ -61,24 +61,24 @@ private void DiagramManager_NodesRemoved(NodeModel[] nodes) } } - private void DiagramManager_GroupAdded(GroupModel group) => group.Changed += Refresh; + private void Diagram_GroupAdded(GroupModel group) => group.Changed += Refresh; - private void DiagramManager_GroupRemoved(GroupModel group) => group.Changed -= Refresh; + private void Diagram_GroupRemoved(GroupModel group) => group.Changed -= Refresh; private void Refresh() { - var nodes = DiagramManager.Nodes - .Union(DiagramManager.Groups) + var nodes = Diagram.Nodes + .Union(Diagram.Groups) .Where(n => n.Size?.Equals(Size.Zero) == false).ToList(); if (nodes.Count == 0) return; var bounds = nodes.GetBounds(); - var nodesMinX = bounds.Left * DiagramManager.Zoom; - var nodesMaxX = bounds.Right * DiagramManager.Zoom; - var nodesMinY = bounds.Top * DiagramManager.Zoom; - var nodesMaxY = bounds.Bottom * DiagramManager.Zoom; + var nodesMinX = bounds.Left * Diagram.Zoom; + var nodesMaxX = bounds.Right * Diagram.Zoom; + var nodesMinY = bounds.Top * Diagram.Zoom; + var nodesMaxY = bounds.Bottom * Diagram.Zoom; (double fullSizeWidth, double fullSizeHeight) = GetFullSize(nodesMaxX, nodesMaxY); AdjustFullSizeWithNodesRect(nodesMinX, nodesMinY, ref fullSizeWidth, ref fullSizeHeight); @@ -94,12 +94,12 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref // Width if (nodesMinX < 0) { - var temp = nodesMinX + DiagramManager.Pan.X; - if (DiagramManager.Pan.X > 0 && temp < 0) + var temp = nodesMinX + Diagram.Pan.X; + if (Diagram.Pan.X > 0 && temp < 0) { fullSizeWidth += Math.Abs(temp); } - else if (DiagramManager.Pan.X <= 0) + else if (Diagram.Pan.X <= 0) { fullSizeWidth += Math.Abs(nodesMinX); } @@ -108,12 +108,12 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref // Height if (nodesMinY < 0) { - var temp = nodesMinY + DiagramManager.Pan.Y; - if (DiagramManager.Pan.Y > 0 && temp < 0) + var temp = nodesMinY + Diagram.Pan.Y; + if (Diagram.Pan.Y > 0 && temp < 0) { fullSizeHeight += Math.Abs(temp); } - else if (DiagramManager.Pan.Y <= 0) + else if (Diagram.Pan.Y <= 0) { fullSizeHeight += Math.Abs(nodesMinY); } @@ -122,24 +122,24 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref private (double width, double height) GetFullSize(double nodesMaxX, double nodesMaxY) { - var nodesLayerWidth = Math.Max(DiagramManager.Container.Width * DiagramManager.Zoom, nodesMaxX); - var nodesLayerHeight = Math.Max(DiagramManager.Container.Height * DiagramManager.Zoom, nodesMaxY); + var nodesLayerWidth = Math.Max(Diagram.Container.Width * Diagram.Zoom, nodesMaxX); + var nodesLayerHeight = Math.Max(Diagram.Container.Height * Diagram.Zoom, nodesMaxY); double fullWidth; double fullHeight; - if (DiagramManager.Zoom == 1) + if (Diagram.Zoom == 1) { - fullWidth = DiagramManager.Container.Width + Math.Abs(DiagramManager.Pan.X); - fullHeight = DiagramManager.Container.Height + Math.Abs(DiagramManager.Pan.Y); + fullWidth = Diagram.Container.Width + Math.Abs(Diagram.Pan.X); + fullHeight = Diagram.Container.Height + Math.Abs(Diagram.Pan.Y); } - else if (DiagramManager.Zoom > 1) + else if (Diagram.Zoom > 1) { // Width - if (DiagramManager.Pan.X < 0) + if (Diagram.Pan.X < 0) { - if (nodesLayerWidth + DiagramManager.Pan.X < DiagramManager.Container.Width) + if (nodesLayerWidth + Diagram.Pan.X < Diagram.Container.Width) { - fullWidth = DiagramManager.Container.Width + Math.Abs(DiagramManager.Pan.X); + fullWidth = Diagram.Container.Width + Math.Abs(Diagram.Pan.X); } else { @@ -148,15 +148,15 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref } else { - fullWidth = nodesLayerWidth + DiagramManager.Pan.X; + fullWidth = nodesLayerWidth + Diagram.Pan.X; } // Height - if (DiagramManager.Pan.Y < 0) + if (Diagram.Pan.Y < 0) { - if (nodesLayerHeight + DiagramManager.Pan.Y < DiagramManager.Container.Height) + if (nodesLayerHeight + Diagram.Pan.Y < Diagram.Container.Height) { - fullHeight = DiagramManager.Container.Height + Math.Abs(DiagramManager.Pan.Y); + fullHeight = Diagram.Container.Height + Math.Abs(Diagram.Pan.Y); } else { @@ -165,29 +165,29 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref } else { - fullHeight = nodesLayerHeight + DiagramManager.Pan.Y; + fullHeight = nodesLayerHeight + Diagram.Pan.Y; } } else { // Width - if (DiagramManager.Pan.X > 0) + if (Diagram.Pan.X > 0) { - fullWidth = Math.Max(nodesLayerWidth + DiagramManager.Pan.X, DiagramManager.Container.Width); + fullWidth = Math.Max(nodesLayerWidth + Diagram.Pan.X, Diagram.Container.Width); } else { - fullWidth = DiagramManager.Container.Width + Math.Abs(DiagramManager.Pan.X); + fullWidth = Diagram.Container.Width + Math.Abs(Diagram.Pan.X); } // Height - if (DiagramManager.Pan.Y > 0) + if (Diagram.Pan.Y > 0) { - fullHeight = Math.Max(nodesLayerHeight + DiagramManager.Pan.Y, DiagramManager.Container.Height); + fullHeight = Math.Max(nodesLayerHeight + Diagram.Pan.Y, Diagram.Container.Height); } else { - fullHeight = DiagramManager.Container.Height + Math.Abs(DiagramManager.Pan.Y); + fullHeight = Diagram.Container.Height + Math.Abs(Diagram.Pan.Y); } } @@ -196,9 +196,9 @@ private void AdjustFullSizeWithNodesRect(double nodesMinX, double nodesMinY, ref public void Dispose() { - DiagramManager.Changed -= DiagramManager_Changed; - DiagramManager.Nodes.Added -= DiagramManager_NodesAdded; - DiagramManager.Nodes.Removed -= DiagramManager_NodesRemoved; + Diagram.Changed -= Diagram_Changed; + Diagram.Nodes.Added -= Diagram_NodesAdded; + Diagram.Nodes.Removed -= Diagram_NodesRemoved; // Todo: unregister node/group changed events } diff --git a/src/Blazor.Diagrams/Components/Renderers/GroupRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/GroupRenderer.cs index 5c1a8a47..c6fc6166 100644 --- a/src/Blazor.Diagrams/Components/Renderers/GroupRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/GroupRenderer.cs @@ -8,15 +8,15 @@ namespace Blazor.Diagrams.Components.Renderers { public class GroupRenderer : ComponentBase { - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Parameter] public GroupModel Group { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { - var componentType = DiagramManager.GetComponentForModel(Group) ?? typeof(DefaultGroupWidget); + var componentType = Diagram.GetComponentForModel(Group) ?? typeof(DefaultGroupWidget); builder.OpenComponent(0, componentType); builder.AddAttribute(1, "Group", Group); builder.CloseComponent(); diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs index 8f64e5b4..feec1775 100644 --- a/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs @@ -14,7 +14,7 @@ namespace Blazor.Diagrams.Components.Renderers { public class LinkLabelRenderer : ComponentBase, IDisposable { - [CascadingParameter(Name = "DiagramManager")] public DiagramManager DiagramManager { get; set; } + [CascadingParameter] public Diagram Diagram { get; set; } [Parameter] public LinkLabelModel Label { get; set; } [Parameter] public SVGPathProperties[] Paths { get; set; } @@ -30,7 +30,7 @@ protected override void OnInitialized() protected override void BuildRenderTree(RenderTreeBuilder builder) { - var component = DiagramManager.GetComponentForModel(Label) ?? typeof(DefaultLinkLabelWidget); + var component = Diagram.GetComponentForModel(Label) ?? typeof(DefaultLinkLabelWidget); var position = FindPosition(); if (position == null) return; diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs index e68e49b4..c398296e 100644 --- a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs @@ -11,8 +11,8 @@ public class LinkRenderer : ComponentBase, IDisposable { private bool _shouldRender = true; - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Parameter] public BaseLinkModel Link { get; set; } @@ -33,8 +33,8 @@ protected override void OnInitialized() protected override void BuildRenderTree(RenderTreeBuilder builder) { - var componentType = DiagramManager.GetComponentForModel(Link) ?? - DiagramManager.Options.Links.DefaultLinkComponent ?? + var componentType = Diagram.GetComponentForModel(Link) ?? + Diagram.Options.Links.DefaultLinkComponent ?? typeof(LinkWidget); builder.OpenElement(0, "g"); @@ -58,8 +58,8 @@ private void Link_Changed() StateHasChanged(); } - private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Link, e); + private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Link, e); - private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Link, e); + private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Link, e); } } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index 39b6b396..aa925cc1 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -20,8 +20,8 @@ public class NodeRenderer : ComponentBase, IDisposable private ElementReference _element; private DotNetObjectReference _reference; - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Parameter] public NodeModel Node { get; set; } @@ -31,9 +31,9 @@ public class NodeRenderer : ComponentBase, IDisposable public void Dispose() { - DiagramManager.PanChanged -= CheckVisibility; - DiagramManager.ZoomChanged -= CheckVisibility; - DiagramManager.ContainerChanged -= CheckVisibility; + Diagram.PanChanged -= CheckVisibility; + Diagram.ZoomChanged -= CheckVisibility; + Diagram.ContainerChanged -= CheckVisibility; Node.Changed -= ReRender; if (_element.Id != null) @@ -49,7 +49,7 @@ public void OnResize(Size size) if (Size.Zero.Equals(size)) return; - size = new Size(size.Width / DiagramManager.Zoom, size.Height / DiagramManager.Zoom); + size = new Size(size.Width / Diagram.Zoom, size.Height / Diagram.Zoom); if (size.Equals(Node.Size)) return; @@ -63,9 +63,9 @@ protected override void OnInitialized() base.OnInitialized(); _reference = DotNetObjectReference.Create(this); - DiagramManager.PanChanged += CheckVisibility; - DiagramManager.ZoomChanged += CheckVisibility; - DiagramManager.ContainerChanged += CheckVisibility; + Diagram.PanChanged += CheckVisibility; + Diagram.ZoomChanged += CheckVisibility; + Diagram.ContainerChanged += CheckVisibility; Node.Changed += ReRender; } @@ -85,8 +85,8 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) if (!_isVisible) return; - var componentType = DiagramManager.GetComponentForModel(Node) ?? - DiagramManager.Options.DefaultNodeComponent ?? + var componentType = Diagram.GetComponentForModel(Node) ?? + Diagram.Options.DefaultNodeComponent ?? (Node.Layer == RenderLayer.HTML ? typeof(NodeWidget) : typeof(SvgNodeWidget)); builder.OpenElement(0, Node.Layer == RenderLayer.HTML ? "div" : "g"); @@ -127,19 +127,19 @@ protected override async Task OnAfterRenderAsync(bool firstRender) private async void CheckVisibility() { // _isVisible must be true in case virtualization gets disabled and some nodes are hidden - if (!DiagramManager.Options.EnableVirtualization && _isVisible) + if (!Diagram.Options.EnableVirtualization && _isVisible) return; if (Node.Size == null) return; - var left = Node.Position.X * DiagramManager.Zoom + DiagramManager.Pan.X; - var top = Node.Position.Y * DiagramManager.Zoom + DiagramManager.Pan.Y; - var right = left + Node.Size.Width * DiagramManager.Zoom; - var bottom = top + Node.Size.Height * DiagramManager.Zoom; + var left = Node.Position.X * Diagram.Zoom + Diagram.Pan.X; + var top = Node.Position.Y * Diagram.Zoom + Diagram.Pan.Y; + var right = left + Node.Size.Width * Diagram.Zoom; + var bottom = top + Node.Size.Height * Diagram.Zoom; - var isVisible = right > 0 && left < DiagramManager.Container.Width && bottom > 0 && - top < DiagramManager.Container.Height; + var isVisible = right > 0 && left < Diagram.Container.Width && bottom > 0 && + top < Diagram.Container.Height; if (_isVisible != isVisible) { @@ -161,8 +161,8 @@ private void ReRender() StateHasChanged(); } - private void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Node, e); + private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Node, e); - private void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Node, e); + private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Node, e); } } \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs index eb8fa9d9..43afc37d 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs @@ -15,8 +15,8 @@ public partial class PortRenderer : IDisposable private bool _shouldRender = true; private ElementReference _element; - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Inject] private IJSRuntime JSRuntime { get; set; } @@ -55,19 +55,19 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } } - protected virtual void OnMouseDown(MouseEventArgs e) => DiagramManager.OnMouseDown(Port, e); + protected virtual void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Port, e); - protected virtual void OnMouseUp(MouseEventArgs e) => DiagramManager.OnMouseUp(Port, e); + protected virtual void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Port, e); private async Task UpdateDimensions() { - var zoom = DiagramManager.Zoom; - var pan = DiagramManager.Pan; + var zoom = Diagram.Zoom; + var pan = Diagram.Pan; var rect = await JSRuntime.GetBoundingClientRect(_element); Port.Size = new Size(rect.Width / zoom, rect.Height / zoom); - Port.Position = new Point((rect.Left - DiagramManager.Container.Left - pan.X) / zoom, - (rect.Top - DiagramManager.Container.Top - pan.Y) / zoom); + Port.Position = new Point((rect.Left - Diagram.Container.Left - pan.X) / zoom, + (rect.Top - Diagram.Container.Top - pan.Y) / zoom); Port.Initialized = true; diff --git a/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs index 337b1a22..8e1cb03d 100644 --- a/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs @@ -13,17 +13,17 @@ public partial class SelectionBoxWidget : IDisposable private Point _selectionBoxTopLeft; private Size _selectionBoxSize; - [CascadingParameter(Name = "DiagramManager")] - public DiagramManager DiagramManager { get; set; } + [CascadingParameter] + public Diagram Diagram { get; set; } [Parameter] public string Background { get; set; } = "rgb(110 159 212 / 25%);"; protected override void OnInitialized() { - DiagramManager.MouseDown += OnMouseDown; - DiagramManager.MouseMove += OnMouseMove; - DiagramManager.MouseUp += OnMouseUp; + Diagram.MouseDown += OnMouseDown; + Diagram.MouseMove += OnMouseMove; + Diagram.MouseUp += OnMouseUp; } private string GenerateStyle() @@ -44,21 +44,21 @@ private void OnMouseMove(Model model, MouseEventArgs e) SetSelectionBoxInformation(e); - var start = DiagramManager.GetRelativeMousePoint(_initialClientPoint.X, _initialClientPoint.Y); - var end = DiagramManager.GetRelativeMousePoint(e.ClientX, e.ClientY); + var start = Diagram.GetRelativeMousePoint(_initialClientPoint.X, _initialClientPoint.Y); + var end = Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY); (var sX, var sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)); (var eX, var eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y)); var bounds = new Rectangle(sX, sY, eX, eY); - foreach (var node in DiagramManager.Nodes) + foreach (var node in Diagram.Nodes) { if (bounds.Overlap(node.GetBounds())) { - DiagramManager.SelectModel(node, false); + Diagram.SelectModel(node, false); } else if (node.Selected) { - DiagramManager.UnselectModel(node); + Diagram.UnselectModel(node); } } @@ -67,8 +67,8 @@ private void OnMouseMove(Model model, MouseEventArgs e) private void SetSelectionBoxInformation(MouseEventArgs e) { - var start = DiagramManager.GetRelativePoint(_initialClientPoint.X, _initialClientPoint.Y); - var end = DiagramManager.GetRelativePoint(e.ClientX, e.ClientY); + var start = Diagram.GetRelativePoint(_initialClientPoint.X, _initialClientPoint.Y); + var end = Diagram.GetRelativePoint(e.ClientX, e.ClientY); (var sX, var sY) = (Math.Min(start.X, end.X), Math.Min(start.Y, end.Y)); (var eX, var eY) = (Math.Max(start.X, end.X), Math.Max(start.Y, end.Y)); _selectionBoxTopLeft = new Point(sX, sY); @@ -85,9 +85,9 @@ private void OnMouseUp(Model model, MouseEventArgs e) public void Dispose() { - DiagramManager.MouseDown -= OnMouseDown; - DiagramManager.MouseMove -= OnMouseMove; - DiagramManager.MouseUp -= OnMouseUp; + Diagram.MouseDown -= OnMouseDown; + Diagram.MouseMove -= OnMouseMove; + Diagram.MouseUp -= OnMouseUp; } } } From 978166141c15f9ef0b89817b5d06c18ece43ee95 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 27 Feb 2021 00:10:10 +0100 Subject: [PATCH 27/67] Update Simple.razor.cs --- samples/SharedDemo/Demos/Simple.razor.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/SharedDemo/Demos/Simple.razor.cs b/samples/SharedDemo/Demos/Simple.razor.cs index 7934d891..efff40a4 100644 --- a/samples/SharedDemo/Demos/Simple.razor.cs +++ b/samples/SharedDemo/Demos/Simple.razor.cs @@ -7,7 +7,7 @@ namespace SharedDemo { public class SimpleComponent : ComponentBase { - protected readonly DiagramManager diagramManager = new DiagramManager(); + protected readonly Diagram diagram = new Diagram(); protected override void OnInitialized() { @@ -15,16 +15,16 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagramManager.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagramManager.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagram.Nodes.Add(node1, node2, NewNode(300, 50)); } - protected void ToggleZoom() => diagramManager.Options.Zoom.Enabled = !diagramManager.Options.Zoom.Enabled; + protected void ToggleZoom() => diagram.Options.Zoom.Enabled = !diagram.Options.Zoom.Enabled; - protected void TogglePanning() => diagramManager.Options.AllowPanning = !diagramManager.Options.AllowPanning; + protected void TogglePanning() => diagram.Options.AllowPanning = !diagram.Options.AllowPanning; protected void ToggleVirtualization() - => diagramManager.Options.EnableVirtualization = !diagramManager.Options.EnableVirtualization; + => diagram.Options.EnableVirtualization = !diagram.Options.EnableVirtualization; private NodeModel NewNode(double x, double y) { From b870b250c34d24bfbb530b4e4ce3aff63ce2d3b8 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 27 Feb 2021 15:29:37 +0100 Subject: [PATCH 28/67] feat: Allow empty groups --- src/Blazor.Diagrams.Core/Diagram.cs | 16 ++++++++-------- .../Extensions/DiagramExtensions.cs | 4 ++++ .../Models/Core/Rectangle.cs | 2 ++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index efb5be84..d524ec24 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -138,15 +138,15 @@ public GroupModel Group(params NodeModel[] children) /// A group instance. public void AddGroup(GroupModel group) { - if (group.Children.Length < 2) - throw new ArgumentException("Number of nodes must be >= 2"); - - var layers = group.Children.Select(n => n.Layer).Distinct(); - if (layers.Count() > 1) - throw new InvalidOperationException("Cannot group nodes with different layers"); + if (group.Children.Length > 0) + { + var layers = group.Children.Select(n => n.Layer).Distinct(); + if (layers.Count() > 1) + throw new InvalidOperationException("Cannot group nodes with different layers"); - if (layers.First() == RenderLayer.SVG) - throw new InvalidOperationException("SVG groups aren't implemented yet"); + if (layers.First() == RenderLayer.SVG) + throw new InvalidOperationException("SVG groups aren't implemented yet"); + } foreach (var child in group.Children) { diff --git a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs index 14a6a211..ac1b01d9 100644 --- a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs +++ b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs @@ -1,6 +1,7 @@ using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Core; using System.Collections.Generic; +using System.Linq; namespace Blazor.Diagrams.Core.Extensions { @@ -8,6 +9,9 @@ public static class DiagramExtensions { public static Rectangle GetBounds(this IEnumerable nodes) { + if (!nodes.Any()) + return Rectangle.Zero; + var minX = double.MaxValue; var maxX = double.MinValue; var minY = double.MaxValue; diff --git a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs b/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs index a22afd64..3c80e4a6 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs @@ -4,6 +4,8 @@ namespace Blazor.Diagrams.Core.Models.Core { public class Rectangle { + public static Rectangle Zero { get; } = new Rectangle(0, 0, 0, 0); + public double Width { get; set; } public double Height { get; set; } public double Top { get; set; } From 0c8f6e20d1c2280165ae7d9e401d030b8bd7a42f Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 27 Feb 2021 16:55:47 +0100 Subject: [PATCH 29/67] fix: JS errors in pages without diagram + Use WebCompiler to minify everything --- src/Blazor.Diagrams/Blazor.Diagrams.csproj | 12 ++++++++- .../wwwroot/default.styles.css | 25 +++++++++++++----- .../wwwroot/default.styles.min.css | 1 + .../wwwroot/default.styles.min.css.gz | Bin 0 -> 657 bytes src/Blazor.Diagrams/wwwroot/script.js | 4 +-- src/Blazor.Diagrams/wwwroot/script.min.js | 2 +- src/Blazor.Diagrams/wwwroot/script.min.js.gz | Bin 0 -> 355 bytes src/Blazor.Diagrams/wwwroot/style.css | 22 +++++++++++---- src/Blazor.Diagrams/wwwroot/style.min.css | 1 + src/Blazor.Diagrams/wwwroot/style.min.css.gz | Bin 0 -> 392 bytes 10 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 src/Blazor.Diagrams/wwwroot/default.styles.min.css create mode 100644 src/Blazor.Diagrams/wwwroot/default.styles.min.css.gz create mode 100644 src/Blazor.Diagrams/wwwroot/script.min.js.gz create mode 100644 src/Blazor.Diagrams/wwwroot/style.min.css create mode 100644 src/Blazor.Diagrams/wwwroot/style.min.css.gz diff --git a/src/Blazor.Diagrams/Blazor.Diagrams.csproj b/src/Blazor.Diagrams/Blazor.Diagrams.csproj index 523cbe8d..c767a58c 100644 --- a/src/Blazor.Diagrams/Blazor.Diagrams.csproj +++ b/src/Blazor.Diagrams/Blazor.Diagrams.csproj @@ -24,9 +24,19 @@ - + + + + + + + + + + + diff --git a/src/Blazor.Diagrams/wwwroot/default.styles.css b/src/Blazor.Diagrams/wwwroot/default.styles.css index dc1135fd..97c9e95b 100644 --- a/src/Blazor.Diagrams/wwwroot/default.styles.css +++ b/src/Blazor.Diagrams/wwwroot/default.styles.css @@ -4,11 +4,18 @@ border-radius: 10px; background-color: #f5f5f5; border: 1px solid #e8e8e8; - box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0,0,0,.12); + -webkit-box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0,0,0,.12); + box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0,0,0,.12); position: relative; + display: -webkit-box; + display: -ms-flexbox; display: flex; - align-items: center; - justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; } .default-node.selected { @@ -105,7 +112,13 @@ padding: 0.25rem; text-align: center; font-size: 0.875rem; - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; min-width: 3rem; - transform: translate(-50%, -50%); -} \ No newline at end of file + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} +/*# sourceMappingURL=wwwroot\default.styles.css.map */ \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/default.styles.min.css b/src/Blazor.Diagrams/wwwroot/default.styles.min.css new file mode 100644 index 00000000..879b2db7 --- /dev/null +++ b/src/Blazor.Diagrams/wwwroot/default.styles.min.css @@ -0,0 +1 @@ +.default-node{width:100px;height:80px;border-radius:10px;background-color:#f5f5f5;border:1px solid #e8e8e8;-webkit-box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12);box-shadow:0 2px 1px -1px rgba(0,0,0,.2),0 1px 1px 0 rgba(0,0,0,.14),0 1px 3px 0 rgba(0,0,0,.12);position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;}.default-node.selected{border:1px solid #6e9fd4;}.default-node.selected .port{border:1px solid #6e9fd4;}.default-node .port,.default.group .port{width:20px;height:20px;margin:-10px;border-radius:50%;background-color:#f5f5f5;border:1px solid #d4d4d4;cursor:pointer;position:absolute;}.default-node .port:hover,.default-node .port.has-links,.default.group .port.has-links{background-color:#000;}.default-node .port.bottom,.default.group .port.bottom{bottom:0;left:50%;}.default-node .port.bottomleft,.default.group .port.bottomleft{bottom:0;left:0;}.default-node .port.bottomright,.default.group .port.bottomright{bottom:0;right:0;}.default-node .port.top,.default.group .port.top{top:0;left:50%;}.default-node .port.topleft,.default.group .port.topleft{top:0;left:0;}.default-node .port.topright,.default.group .port.topright{top:0;right:0;}.default-node .port.left,.default.group .port.left{left:0;top:50%;}.default-node .port.right,.default.group .port.right{right:0;top:50%;}.diagram-navigator.default{position:absolute;bottom:10px;right:10px;border:3px solid #9ba8b0;border-radius:15px;padding:20px;background-color:#fff;}.diagram-navigator.default .current-view{border:2px solid #40babd;}.group.default{outline:2px solid #000;background:#c6c6c6;}.group.default.selected{outline:2px solid #6e9fd4;}.link .link-label{display:inline-block;color:#fff;background-color:#6e9fd4;border-radius:.25rem;padding:.25rem;text-align:center;font-size:.875rem;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;min-width:3rem;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);} \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/default.styles.min.css.gz b/src/Blazor.Diagrams/wwwroot/default.styles.min.css.gz new file mode 100644 index 0000000000000000000000000000000000000000..27c95b48cab9b5a67fb03369e22d5be1633af9b8 GIT binary patch literal 657 zcmV;C0&e{uiwFP!000003bj>jj@uv*y-K90(nbmv`PydjX|NePG#D(vNp_<>S%36M zy+Q%ov9orZ_D6;6n0b%K49rY^|NIp($Y>=jl8V9g%$Yrq6~^TyJ^;@TmhAm^qBH}I zG-bRp4)-RS9&@cK$xy0<(qx|P{AjpjRbFOB3C?D7*!vNqGbBfDQKBx$94J#~f@f<- z^Hk(tI#1{eUiuNO7fT!v|KK}lwQaG@5L+*t2mg74QW2&bToSYzX5sd@a1;-qKV-_Mj_Aasgh`bg5Ma$L#*SNA-P(QzIJ z*noYZ$9_!2)uN5iWJHg>39d9wc%g?eO?Xnf(K^sm;WXE@K$4z#POZ}I>gzDao0|zm zRV(%Ci)^~0c}wU%!C(Ihc8*(8#< { - s.canvas.ref.invokeMethodAsync('OnResize', s.canvas.elem.getBoundingClientRect()); + s.canvas && s.canvas.ref.invokeMethodAsync('OnResize', s.canvas.elem.getBoundingClientRect()); }); \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js b/src/Blazor.Diagrams/wwwroot/script.min.js index 2f2d782d..7202df09 100644 --- a/src/Blazor.Diagrams/wwwroot/script.min.js +++ b/src/Blazor.Diagrams/wwwroot/script.min.js @@ -1 +1 @@ -var s={canvas:null,getBoundingClientRect:e=>e.getBoundingClientRect(),tracked:{},ro:new ResizeObserver(e=>{for(const n of e){let e=Array.from(n.target.attributes).find(e=>e.name.startsWith("_bl")).name.substring(4),t=window.ZBlazorDiagrams.tracked[e];t&&t.ref.invokeMethodAsync("OnResize",n.target.getBoundingClientRect())}}),observe:(e,n,t,r)=>{s.ro.observe(e),s.tracked[t]={ref:n},r&&(s.canvas={elem:e,ref:n})},unobserve:(e,n)=>{s.ro.unobserve(e),delete s.tracked[n]}};window.ZBlazorDiagrams=s,window.addEventListener("scroll",()=>{s.canvas.ref.invokeMethodAsync("OnResize",s.canvas.elem.getBoundingClientRect())}); \ No newline at end of file +var s={canvas:null,getBoundingClientRect:n=>n.getBoundingClientRect(),tracked:{},ro:new ResizeObserver(n=>{for(const t of n){let i=Array.from(t.target.attributes).find(n=>n.name.startsWith("_bl")).name.substring(4),n=window.ZBlazorDiagrams.tracked[i];n&&n.ref.invokeMethodAsync("OnResize",t.target.getBoundingClientRect())}}),observe:(n,t,i,r)=>{s.ro.observe(n),s.tracked[i]={ref:t},r&&(s.canvas={elem:n,ref:t})},unobserve:(n,t)=>{s.ro.unobserve(n),delete s.tracked[t]}};window.ZBlazorDiagrams=s;window.addEventListener("scroll",()=>{s.canvas&&s.canvas.ref.invokeMethodAsync("OnResize",s.canvas.elem.getBoundingClientRect())}); \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js.gz b/src/Blazor.Diagrams/wwwroot/script.min.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..aa01cabc44d1a902d7a94c0965e7a5442a8a236f GIT binary patch literal 355 zcmV-p0i6CHiwFP!000003WZTYYQr!P{EG3xNFa3T$-#xB?X4w{OQ9tc#a=ri$_iF% zhs5}0J@iTaLKU2(IW+Vpc6MfVM&F-bouMQYeQB^WA;;D^)d0_Yi24 z_o)Abgj7tX+(4E0gCd_J?2-ipdw}^W0ChkjvisVTC_M&FIPrCYvUk9VEv7_fuWRyK z!I}+46K$Buu3Cm5b#1Xa3ZO=_1s&oFhgZvMk$$XPD&=Y3u0kYXBkrWaVi#F{r{8AI z96UW*(~#K)eY*7BE+?2}s0nIqvGW^v0bcuR8unOfl^(27k%{2IkuH$BIvRGq6Y`t9My=)LA7`-d=b5jkFr%HF(kk0yc~wf z-|35R2252wbuq)U4Gb7RC=Dff=Tap`oAIP9JHP%f>auxEal0)!`2h_ve#v$M003|A Bp%VZA literal 0 HcmV?d00001 diff --git a/src/Blazor.Diagrams/wwwroot/style.css b/src/Blazor.Diagrams/wwwroot/style.css index 3fa888d6..737d479b 100644 --- a/src/Blazor.Diagrams/wwwroot/style.css +++ b/src/Blazor.Diagrams/wwwroot/style.css @@ -4,6 +4,7 @@ position: relative; outline: none; overflow: hidden; + cursor: -webkit-grab; cursor: grab; } @@ -14,7 +15,9 @@ bottom: 0px; position: absolute; pointer-events: none; - transform-origin: 0px 0px; + -webkit-transform-origin: 0px 0px; + -ms-transform-origin: 0px 0px; + transform-origin: 0px 0px; width: 100%; height: 100%; overflow: visible; @@ -23,7 +26,9 @@ .html-layer, .svg-layer { position: absolute; pointer-events: none; - transform-origin: 0px 0px; + -webkit-transform-origin: 0px 0px; + -ms-transform-origin: 0px 0px; + transform-origin: 0px 0px; width: 100%; height: 100%; overflow: visible; @@ -31,7 +36,10 @@ .node { position: absolute; - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; cursor: move; pointer-events: all; } @@ -60,7 +68,10 @@ .group { position: absolute; - user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; cursor: move; pointer-events: all; } @@ -74,4 +85,5 @@ .link foreignObject { overflow: visible; pointer-events: none; -} \ No newline at end of file +} +/*# sourceMappingURL=wwwroot\style.css.map */ \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/style.min.css b/src/Blazor.Diagrams/wwwroot/style.min.css new file mode 100644 index 00000000..9d5df7cc --- /dev/null +++ b/src/Blazor.Diagrams/wwwroot/style.min.css @@ -0,0 +1 @@ +.diagram-canvas{width:100%;height:100%;position:relative;outline:none;overflow:hidden;cursor:-webkit-grab;cursor:grab;}.diagram-svg-layer,.diagram-html-layer{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;width:100%;height:100%;overflow:visible;}.html-layer,.svg-layer{position:absolute;pointer-events:none;-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;width:100%;height:100%;overflow:visible;}.node{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:move;pointer-events:all;}.node.locked{cursor:pointer;}.link{pointer-events:visiblePainted;cursor:pointer;}.link path.selection-helper:hover{stroke-opacity:.05;}.diagram-navigator{z-index:10;}.diagram-navigator .current-view{position:absolute;border:2px solid #000;}.group{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:move;pointer-events:all;}.group .layer{position:absolute;overflow:visible;pointer-events:none;}.link foreignObject{overflow:visible;pointer-events:none;} \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/style.min.css.gz b/src/Blazor.Diagrams/wwwroot/style.min.css.gz new file mode 100644 index 0000000000000000000000000000000000000000..9b86c3f97b05fd959462136e78f06595af0193c7 GIT binary patch literal 392 zcmV;30eAi%iwFP!000003eA#1PQx$|MXwSGv8yA4#Kt)UT)~N_$xz1=*^?9+aWWPh zi7PN|(}3DkV%b$De{9d2umAn|l{>U`w5_Ua7;GB1=(sTtTHhB9VBPSs?kFK61rxzr z#sLcI*<%0`D9FzMaqVeq8gvdqQS~uVG-?aw23g5~@}wQ=k9lkwYUS-a#MLY{-1?(7 zGId559@Z?Mc)-y`NzBwrF+ Date: Sat, 27 Feb 2021 18:28:15 +0100 Subject: [PATCH 30/67] fix: Compare sizes with precision of 0.00001 --- .../Extensions/DoubleExtensions.cs | 10 ++++++++++ src/Blazor.Diagrams.Core/Models/Core/Size.cs | 6 ++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs diff --git a/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs new file mode 100644 index 00000000..ea6b79e5 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs @@ -0,0 +1,10 @@ +using System; + +namespace Blazor.Diagrams.Core.Extensions +{ + public static class DoubleExtensions + { + public static bool AlmostEqualTo(this double double1, double double2, double tolerance = 0.00001) + => Math.Abs(double1 - double2) < tolerance; + } +} diff --git a/src/Blazor.Diagrams.Core/Models/Core/Size.cs b/src/Blazor.Diagrams.Core/Models/Core/Size.cs index 32559404..76aca894 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Size.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Size.cs @@ -1,4 +1,6 @@ -namespace Blazor.Diagrams.Core.Models.Core +using Blazor.Diagrams.Core.Extensions; + +namespace Blazor.Diagrams.Core.Models.Core { public class Size { @@ -17,7 +19,7 @@ public Size(double width, double height) public Size Add(double value) => new Size(Width + value, Height + value); - public bool Equals(Size? size) => size != null && Width == size.Width && Height == size.Height; + public bool Equals(Size? size) => size != null && Width.AlmostEqualTo(size.Width) && Height.AlmostEqualTo(size.Height); public override string ToString() => $"Size(width={Width}, height={Height})"; } From f473ab6fb2d024aaea19f6641c65a0d0aa72c427 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 27 Feb 2021 19:55:07 +0100 Subject: [PATCH 31/67] refactor: Move size check with tolerance to NodeRenderer & increase default tolerance --- src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs | 2 +- src/Blazor.Diagrams.Core/Models/Core/Size.cs | 6 ++---- src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs | 3 ++- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs index ea6b79e5..9f3832ba 100644 --- a/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs +++ b/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs @@ -4,7 +4,7 @@ namespace Blazor.Diagrams.Core.Extensions { public static class DoubleExtensions { - public static bool AlmostEqualTo(this double double1, double double2, double tolerance = 0.00001) + public static bool AlmostEqualTo(this double double1, double double2, double tolerance = 0.0001) => Math.Abs(double1 - double2) < tolerance; } } diff --git a/src/Blazor.Diagrams.Core/Models/Core/Size.cs b/src/Blazor.Diagrams.Core/Models/Core/Size.cs index 76aca894..32559404 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Size.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Size.cs @@ -1,6 +1,4 @@ -using Blazor.Diagrams.Core.Extensions; - -namespace Blazor.Diagrams.Core.Models.Core +namespace Blazor.Diagrams.Core.Models.Core { public class Size { @@ -19,7 +17,7 @@ public Size(double width, double height) public Size Add(double value) => new Size(Width + value, Height + value); - public bool Equals(Size? size) => size != null && Width.AlmostEqualTo(size.Width) && Height.AlmostEqualTo(size.Height); + public bool Equals(Size? size) => size != null && Width == size.Width && Height == size.Height; public override string ToString() => $"Size(width={Width}, height={Height})"; } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index aa925cc1..d374ef84 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -50,9 +50,10 @@ public void OnResize(Size size) return; size = new Size(size.Width / Diagram.Zoom, size.Height / Diagram.Zoom); - if (size.Equals(Node.Size)) + if (Node.Size != null && Node.Size.Width.AlmostEqualTo(size.Width) && Node.Size.Height.AlmostEqualTo(size.Height)) return; + Console.WriteLine($"Update! {Node.Size} vs {size}"); Node.Size = size; Node.Refresh(); Node.ReinitializePorts(); From bdf9ecc84b90d6503c96ffe77245fe75bb81a66f Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 1 Mar 2021 12:56:57 +0100 Subject: [PATCH 32/67] perf: Update ports dimensions only if Initialized is false --- src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs index 43afc37d..c126917e 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs @@ -49,7 +49,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) await base.OnAfterRenderAsync(firstRender); _shouldRender = false; - if (!Port.Initialized || firstRender) + if (!Port.Initialized) { await UpdateDimensions(); } From 407602fa920d035a0e88d74798c47821b9aa5486 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Tue, 2 Mar 2021 12:13:58 +0100 Subject: [PATCH 33/67] feat: observe body mutations to update canvas position --- .../Components/DiagramCanvas.razor.cs | 2 +- .../Components/Renderers/NodeRenderer.cs | 1 - .../Extensions/JSRuntimeExtensions.cs | 4 +- src/Blazor.Diagrams/wwwroot/script.js | 42 +++++++++++++----- src/Blazor.Diagrams/wwwroot/script.min.js | 2 +- src/Blazor.Diagrams/wwwroot/script.min.js.gz | Bin 355 -> 517 bytes 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs index 11d40c12..bdde30e7 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs @@ -45,7 +45,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) if (firstRender) { Diagram.Container = await JSRuntime.GetBoundingClientRect(elementReference); - await JSRuntime.ObserveResizes(elementReference, _reference, isCanvas: true); + await JSRuntime.ObserveResizes(elementReference, _reference); } } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index d374ef84..a37e2b5a 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -53,7 +53,6 @@ public void OnResize(Size size) if (Node.Size != null && Node.Size.Width.AlmostEqualTo(size.Width) && Node.Size.Height.AlmostEqualTo(size.Height)) return; - Console.WriteLine($"Update! {Node.Size} vs {size}"); Node.Size = size; Node.Refresh(); Node.ReinitializePorts(); diff --git a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs index 239ae03d..0e0e9062 100644 --- a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs +++ b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs @@ -13,9 +13,9 @@ public static async Task GetBoundingClientRect(this IJSRuntime jsRunt } public static async Task ObserveResizes(this IJSRuntime jsRuntime, ElementReference element, - DotNetObjectReference reference, bool isCanvas = false) where T : class + DotNetObjectReference reference) where T : class { - await jsRuntime.InvokeVoidAsync("ZBlazorDiagrams.observe", element, reference, element.Id, isCanvas); + await jsRuntime.InvokeVoidAsync("ZBlazorDiagrams.observe", element, reference, element.Id); } public static async Task UnobserveResizes(this IJSRuntime jsRuntime, ElementReference element) diff --git a/src/Blazor.Diagrams/wwwroot/script.js b/src/Blazor.Diagrams/wwwroot/script.js index a7e96594..c47a5dfb 100644 --- a/src/Blazor.Diagrams/wwwroot/script.js +++ b/src/Blazor.Diagrams/wwwroot/script.js @@ -1,37 +1,55 @@ var s = { - canvas: null, + canvases: {}, + tracked: {}, getBoundingClientRect: el => { return el.getBoundingClientRect(); }, - tracked: {}, + mo: new MutationObserver(() => { + for (id in s.canvases) { + const canvas = s.canvases[id]; + const lastBounds = canvas.lastBounds; + const bounds = canvas.elem.getBoundingClientRect(); + if (lastBounds.left !== bounds.left || lastBounds.top !== bounds.top || lastBounds.width !== bounds.width || + lastBounds.height !== bounds.height) { + canvas.lastBounds = bounds; + canvas.ref.invokeMethodAsync('OnResize', bounds); + } + } + }), ro: new ResizeObserver(entries => { for (const entry of entries) { let id = Array.from(entry.target.attributes).find(e => e.name.startsWith('_bl')).name.substring(4); - let element = window.ZBlazorDiagrams.tracked[id]; + let element = s.tracked[id]; if (element) { element.ref.invokeMethodAsync('OnResize', entry.target.getBoundingClientRect()); } } }), - observe: (element, ref, id, isCanvas) => { + observe: (element, ref, id) => { s.ro.observe(element); s.tracked[id] = { ref: ref }; - if (isCanvas) { - s.canvas = { + if (element.classList.contains("diagram-canvas")) { + s.canvases[id] = { elem: element, - ref: ref - } + ref: ref, + lastBounds: element.getBoundingClientRect() + }; } }, unobserve: (element, id) => { s.ro.unobserve(element); delete s.tracked[id]; - }, - + delete s.canvases[id]; + } }; window.ZBlazorDiagrams = s; window.addEventListener('scroll', () => { - s.canvas && s.canvas.ref.invokeMethodAsync('OnResize', s.canvas.elem.getBoundingClientRect()); -}); \ No newline at end of file + for (id in s.canvases) { + const canvas = s.canvases[id]; + canvas.lastBounds = canvas.elem.getBoundingClientRect(); + canvas.ref.invokeMethodAsync('OnResize', canvas.lastBounds); + } +}); +s.mo.observe(document.body, { childList: true, subtree: true }); \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js b/src/Blazor.Diagrams/wwwroot/script.min.js index 7202df09..c3027f1b 100644 --- a/src/Blazor.Diagrams/wwwroot/script.min.js +++ b/src/Blazor.Diagrams/wwwroot/script.min.js @@ -1 +1 @@ -var s={canvas:null,getBoundingClientRect:n=>n.getBoundingClientRect(),tracked:{},ro:new ResizeObserver(n=>{for(const t of n){let i=Array.from(t.target.attributes).find(n=>n.name.startsWith("_bl")).name.substring(4),n=window.ZBlazorDiagrams.tracked[i];n&&n.ref.invokeMethodAsync("OnResize",t.target.getBoundingClientRect())}}),observe:(n,t,i,r)=>{s.ro.observe(n),s.tracked[i]={ref:t},r&&(s.canvas={elem:n,ref:t})},unobserve:(n,t)=>{s.ro.unobserve(n),delete s.tracked[t]}};window.ZBlazorDiagrams=s;window.addEventListener("scroll",()=>{s.canvas&&s.canvas.ref.invokeMethodAsync("OnResize",s.canvas.elem.getBoundingClientRect())}); \ No newline at end of file +var s={canvases:{},tracked:{},getBoundingClientRect:n=>n.getBoundingClientRect(),mo:new MutationObserver(()=>{for(id in s.canvases){const t=s.canvases[id],i=t.lastBounds,n=t.elem.getBoundingClientRect();(i.left!==n.left||i.top!==n.top||i.width!==n.width||i.height!==n.height)&&(t.lastBounds=n,t.ref.invokeMethodAsync("OnResize",n))}}),ro:new ResizeObserver(n=>{for(const t of n){let i=Array.from(t.target.attributes).find(n=>n.name.startsWith("_bl")).name.substring(4),n=s.tracked[i];n&&n.ref.invokeMethodAsync("OnResize",t.target.getBoundingClientRect())}}),observe:(n,t,i)=>{s.ro.observe(n),s.tracked[i]={ref:t},n.classList.contains("diagram-canvas")&&(s.canvases[i]={elem:n,ref:t,lastBounds:n.getBoundingClientRect()})},unobserve:(n,t)=>{s.ro.unobserve(n),delete s.tracked[t],delete s.canvases[t]}};window.ZBlazorDiagrams=s;window.addEventListener("scroll",()=>{for(id in s.canvases){const n=s.canvases[id];n.lastBounds=n.elem.getBoundingClientRect();n.ref.invokeMethodAsync("OnResize",n.lastBounds)}});s.mo.observe(document.body,{childList:!0,subtree:!0}); \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js.gz b/src/Blazor.Diagrams/wwwroot/script.min.js.gz index aa01cabc44d1a902d7a94c0965e7a5442a8a236f..1c87080a9512a58bcc56ed90ab5de2335543177d 100644 GIT binary patch literal 517 zcmV+g0{Z{q%Lr*dkAZdGR5FnQV4UB>o*VZD^ zvLIJB97Qkdp-<`;N=lBcL*4YG!I zM;2-Ou|XhyfQkz$uSx&)N=VhZf?%KBb!-?N-LC`q9e5#RdEIZE7icr2Ea+EC*;kGN zXI!4KN3<)2CF{n7i6$tDM`)nE#Jm)!8`$twS<+a%yr5?HV?4)iI_}YOJpyBupc-&n zPmiWjE*4@=T#{n#VWW|D?g{RIYiDo5fhv*T(*p#2hFp=9!yuKP)KBP1I~}DTg=TJ( zk?b4b49gqu%|UOxYa=TgAJu8Y>~Y<3T#nu#+0=kEnHF@2zi{}9T#Nksy2+(H;@vvL zN3<0mWL#CyM;9M)wWP&@-d1p8zp&p?pPM8VA}&Q?vNUM#^br;$)ttHPBWuBfB3;FW zhA$Xc$0e{q3L>{?w%)YwCfD<{_<024^W=9yYV4xUmJ}D`H^@PCG@tS`;|Wev+Sr*P zI}gjNGj>YPd?2f zzl_11T~_`{}i_2h~?KHa3YUuHLH{C-wlbjIaIxHUl^o HCj24 z_o)Abgj7tX+(4E0gCd_J?2-ipdw}^W0ChkjvisVTC_M&FIPrCYvUk9VEv7_fuWRyK z!I}+46K$Buu3Cm5b#1Xa3ZO=_1s&oFhgZvMk$$XPD&=Y3u0kYXBkrWaVi#F{r{8AI z96UW*(~#K)eY*7BE+?2}s0nIqvGW^v0bcuR8unOfl^(27k%{2IkuH$BIvRGq6Y`t9My=)LA7`-d=b5jkFr%HF(kk0yc~wf z-|35R2252wbuq)U4Gb7RC=Dff=Tap`oAIP9JHP%f>auxEal0)!`2h_ve#v$M003|A Bp%VZA From c37391226a891ce4a6602e4d2fbb0ee6e04916da Mon Sep 17 00:00:00 2001 From: zHaytam Date: Tue, 2 Mar 2021 12:35:49 +0100 Subject: [PATCH 34/67] fix: CustomNodeWidget was movable from text input --- samples/SharedDemo/Demos/BotAnswerWidget.razor | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/samples/SharedDemo/Demos/BotAnswerWidget.razor b/samples/SharedDemo/Demos/BotAnswerWidget.razor index c9f4675b..a2c6ad93 100644 --- a/samples/SharedDemo/Demos/BotAnswerWidget.razor +++ b/samples/SharedDemo/Demos/BotAnswerWidget.razor @@ -3,7 +3,12 @@
Bot Answer
- +
From 9adb4ded0e7912ab367521e8a621b350eb392e5a Mon Sep 17 00:00:00 2001 From: zHaytam Date: Tue, 2 Mar 2021 13:36:33 +0100 Subject: [PATCH 35/67] feat: Add ability to add/remove children after the group is created --- .../Behaviors/GroupingBehavior.cs | 1 - src/Blazor.Diagrams.Core/Diagram.cs | 4 +- src/Blazor.Diagrams.Core/Models/GroupModel.cs | 47 ++++++++++++++++--- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs index 236d85af..8b69f66e 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/GroupingBehavior.cs @@ -19,7 +19,6 @@ private void Diagram_KeyDown(KeyboardEventArgs e) if (!Diagram.GetSelectedModels().Any()) return; - Console.WriteLine(Diagram.Options.Groups.KeyboardShortcut(e)); if (!Diagram.Options.Groups.KeyboardShortcut(e)) return; diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index d524ec24..fc28a58f 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -138,7 +138,7 @@ public GroupModel Group(params NodeModel[] children) /// A group instance. public void AddGroup(GroupModel group) { - if (group.Children.Length > 0) + if (group.Children.Count > 0) { var layers = group.Children.Select(n => n.Layer).Distinct(); if (layers.Count() > 1) @@ -178,7 +178,7 @@ public void RemoveGroup(GroupModel group) // Todo: batch Refresh() group.Ungroup(); - Nodes.Remove(group.Children); + Nodes.Remove(group.Children.ToArray()); Links.Remove(group.AllLinks.ToArray()); GroupRemoved?.Invoke(group); Refresh(); diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs index fb7ca6c9..068be035 100644 --- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs +++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs @@ -8,18 +8,48 @@ namespace Blazor.Diagrams.Core.Models { public class GroupModel : NodeModel { - public GroupModel(NodeModel[] children, byte padding = 30) + private readonly List _children; + + public GroupModel(IEnumerable children, byte padding = 30) { + _children = new List(); + Size = Size.Zero; - Children = children; Padding = padding; - Initialize(); + Initialize(children); } - public NodeModel[] Children { get; private set; } + public IReadOnlyList Children => _children; public byte Padding { get; } + public IEnumerable HandledLinks => _children.SelectMany(c => c.AllLinks).Distinct(); + + public void AddChild(NodeModel child) + { + _children.Add(child); + child.Group = this; + child.SizeChanged += OnNodeChanged; + child.Moving += OnNodeChanged; + + if (UpdateDimensions()) + { + Refresh(); + } + } - public IEnumerable HandledLinks => Children.SelectMany(c => c.AllLinks).Distinct(); + public void RemoveChild(NodeModel child) + { + if (!_children.Remove(child)) + return; + + child.Group = null; + child.SizeChanged -= OnNodeChanged; + child.Moving -= OnNodeChanged; + + if (UpdateDimensions()) + { + Refresh(); + } + } public override void SetPosition(double x, double y) { @@ -49,12 +79,15 @@ public void Ungroup() child.SizeChanged -= OnNodeChanged; child.Moving -= OnNodeChanged; } + + _children.Clear(); } - private void Initialize() + private void Initialize(IEnumerable children) { - foreach (var child in Children) + foreach (var child in children) { + _children.Add(child); child.Group = this; child.SizeChanged += OnNodeChanged; child.Moving += OnNodeChanged; From acb0cd71c3f62d6cbd1023d3563e36d2adae1ba1 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Thu, 4 Mar 2021 09:59:35 +0100 Subject: [PATCH 36/67] refactor/feat: SuspendRefresh on diagrams + batch + separate Layers --- .../ReconnectLinksToClosestPorts.razor.cs | 2 +- .../Demos/CustomGroup/Demo.razor.cs | 2 +- .../SharedDemo/Demos/CustomLink/Demo.razor.cs | 2 +- samples/SharedDemo/Demos/CustomNode.razor.cs | 2 +- .../SharedDemo/Demos/CustomPort/Demo.razor.cs | 2 +- .../Demos/DynamicInsertions.razor.cs | 5 +- samples/SharedDemo/Demos/Events.razor.cs | 11 +- samples/SharedDemo/Demos/Grouping.razor.cs | 2 +- samples/SharedDemo/Demos/Locked.razor.cs | 2 +- samples/SharedDemo/Demos/Performance.razor.cs | 2 +- samples/SharedDemo/Demos/Simple.razor.cs | 2 +- samples/SharedDemo/Demos/SnapToGrid.razor.cs | 2 +- samples/SharedDemo/Demos/ZoomToFit.razor.cs | 2 +- .../Behaviors/DebugEventsBehavior.cs | 16 +-- src/Blazor.Diagrams.Core/Diagram.cs | 68 ++++------- src/Blazor.Diagrams.Core/Layer.cs | 56 --------- src/Blazor.Diagrams.Core/Layers/BaseLayer.cs | 108 ++++++++++++++++++ src/Blazor.Diagrams.Core/Layers/LinkLayer.cs | 33 ++++++ src/Blazor.Diagrams.Core/Layers/NodeLayer.cs | 21 ++++ src/Blazor.Diagrams.Core/Models/NodeModel.cs | 1 - src/Blazor.Diagrams.Core/Models/PortModel.cs | 3 +- .../Components/NavigatorWidget.razor.cs | 14 +-- 22 files changed, 214 insertions(+), 144 deletions(-) delete mode 100644 src/Blazor.Diagrams.Core/Layer.cs create mode 100644 src/Blazor.Diagrams.Core/Layers/BaseLayer.cs create mode 100644 src/Blazor.Diagrams.Core/Layers/LinkLayer.cs create mode 100644 src/Blazor.Diagrams.Core/Layers/NodeLayer.cs diff --git a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs index a9c16490..8e3e0c9c 100644 --- a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs +++ b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs @@ -19,7 +19,7 @@ protected override void OnInitialized() var node3 = NewNode(300, 50); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Right))); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Bottom), node3.GetPort(PortAlignment.Top))); - diagram.Nodes.Add(node1, node2, node3); + diagram.Nodes.Add(new[] { node1, node2, node3 }); } diff --git a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs index 9a329633..0b4c374b 100644 --- a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs @@ -22,7 +22,7 @@ protected override void OnInitialized() var node2 = NewNode(300, 300); var node3 = NewNode(500, 100); - _diagram.Nodes.Add(node1, node2, node3); + _diagram.Nodes.Add(new[] { node1, node2, node3 }); _diagram.AddGroup(new CustomGroupModel(new[] { node2, node3 }, "Group 1")); _diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); diff --git a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs index 9e04bc6a..6a3fecc0 100644 --- a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs @@ -23,7 +23,7 @@ protected override void OnInitialized() var node2 = NewNode(300, 300); var node3 = NewNode(500, 50); - _diagram.Nodes.Add(node1, node2, node3); + _diagram.Nodes.Add(new[] { node1, node2, node3 }); _diagram.Links.Add(new ThickLink(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); _diagram.Links.Add(new ThickLink(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left))); } diff --git a/samples/SharedDemo/Demos/CustomNode.razor.cs b/samples/SharedDemo/Demos/CustomNode.razor.cs index 87195736..72747711 100644 --- a/samples/SharedDemo/Demos/CustomNode.razor.cs +++ b/samples/SharedDemo/Demos/CustomNode.razor.cs @@ -21,7 +21,7 @@ protected override void OnInitialized() node.AddPort(PortAlignment.Bottom); node.AddPort(PortAlignment.Left); - diagram.Nodes.Add(node, NewNode(100, 100), NewNode(300, 300)); + diagram.Nodes.Add(new[] { node, NewNode(100, 100), NewNode(300, 300) }); } private BotAnswerNode NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs index a23b6279..36d54d44 100644 --- a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs @@ -21,7 +21,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - _diagram.Nodes.Add(node1, node2, NewNode(500, 50)); + _diagram.Nodes.Add(new[] { node1, node2, NewNode(500, 50) }); _diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Top), node2.GetPort(PortAlignment.Top))); } diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs index bd1fea1e..d52b6a22 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs @@ -17,9 +17,8 @@ protected override void OnInitialized() base.OnInitialized(); diagram.Options.Groups.Enabled = true; - var node1 = new NodeModel(new Point(300, 50)); - var node2 = new NodeModel(new Point(300, 400)); - diagram.Nodes.Add(node1, node2); + diagram.Nodes.Add(new NodeModel(new Point(300, 50))); + diagram.Nodes.Add(new NodeModel(new Point(300, 400))); } protected void AddNode() diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index b129b19b..339e4e06 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -3,7 +3,6 @@ using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using System.Collections.Generic; -using System.Linq; namespace SharedDemo.Demos { @@ -20,7 +19,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagram.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); } @@ -32,8 +31,8 @@ private void RegisterEvents() StateHasChanged(); }; - diagram.Nodes.Added += (nodes) => events.AddRange(nodes.Select(n => $"NodesAdded, NodeId={n.Id}")); - diagram.Nodes.Removed += (nodes) => events.AddRange(nodes.Select(n => $"NodesRemoved, NodeId={n.Id}")); + diagram.Nodes.Added += (n) => events.Add($"NodesAdded, NodeId={n.Id}"); + diagram.Nodes.Removed += (n) => events.Add($"NodesRemoved, NodeId={n.Id}"); diagram.SelectionChanged += (m) => { @@ -41,7 +40,7 @@ private void RegisterEvents() StateHasChanged(); }; - diagram.Links.Added += (links) => events.AddRange(links.Select(l => $"Links.Added, LinkId={l.Id}")); + diagram.Links.Added += (l) => events.Add($"Links.Added, LinkId={l.Id}"); // Todo: replace with TargetPortChanged //diagram.LinkAttached += (l) => @@ -50,7 +49,7 @@ private void RegisterEvents() // StateHasChanged(); //}; - diagram.Links.Removed += (links) => events.AddRange(links.Select(l => $"Links.Removed, LinkId={l.Id}")); + diagram.Links.Removed += (l) => events.Add($"Links.Removed, LinkId={l.Id}"); diagram.MouseUp += (m, e) => { diff --git a/samples/SharedDemo/Demos/Grouping.razor.cs b/samples/SharedDemo/Demos/Grouping.razor.cs index 2d661fa4..bc7caee3 100644 --- a/samples/SharedDemo/Demos/Grouping.razor.cs +++ b/samples/SharedDemo/Demos/Grouping.razor.cs @@ -17,7 +17,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(250, 250); var node3 = NewNode(500, 100); - diagram.Nodes.Add(node1, node2, node3); + diagram.Nodes.Add(new[] { node1, node2, node3 }); var group = diagram.Group(node1, node2); group.AddPort(PortAlignment.Bottom); diff --git a/samples/SharedDemo/Demos/Locked.razor.cs b/samples/SharedDemo/Demos/Locked.razor.cs index 1621dbe4..cbed95b2 100644 --- a/samples/SharedDemo/Demos/Locked.razor.cs +++ b/samples/SharedDemo/Demos/Locked.razor.cs @@ -15,7 +15,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagram.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); var link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) { diff --git a/samples/SharedDemo/Demos/Performance.razor.cs b/samples/SharedDemo/Demos/Performance.razor.cs index bcb01fca..6d935db1 100644 --- a/samples/SharedDemo/Demos/Performance.razor.cs +++ b/samples/SharedDemo/Demos/Performance.razor.cs @@ -23,7 +23,7 @@ protected override void OnInitialized() var sourcePort = node1.AddPort(PortAlignment.Right); var targetPort = node2.AddPort(PortAlignment.Left); - diagram.Nodes.Add(node1, node2); + diagram.Nodes.Add(new[] { node1, node2 }); diagram.Links.Add(new LinkModel(sourcePort, targetPort)); } } diff --git a/samples/SharedDemo/Demos/Simple.razor.cs b/samples/SharedDemo/Demos/Simple.razor.cs index efff40a4..309305e0 100644 --- a/samples/SharedDemo/Demos/Simple.razor.cs +++ b/samples/SharedDemo/Demos/Simple.razor.cs @@ -16,7 +16,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagram.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); } protected void ToggleZoom() => diagram.Options.Zoom.Enabled = !diagram.Options.Zoom.Enabled; diff --git a/samples/SharedDemo/Demos/SnapToGrid.razor.cs b/samples/SharedDemo/Demos/SnapToGrid.razor.cs index beaada30..c7ced383 100644 --- a/samples/SharedDemo/Demos/SnapToGrid.razor.cs +++ b/samples/SharedDemo/Demos/SnapToGrid.razor.cs @@ -19,7 +19,7 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagram.Nodes.Add(node1, node2, NewNode(300, 50)); + diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); } private NodeModel NewNode(double x, double y) diff --git a/samples/SharedDemo/Demos/ZoomToFit.razor.cs b/samples/SharedDemo/Demos/ZoomToFit.razor.cs index 1fa2ad2a..0bd1289b 100644 --- a/samples/SharedDemo/Demos/ZoomToFit.razor.cs +++ b/samples/SharedDemo/Demos/ZoomToFit.razor.cs @@ -23,7 +23,7 @@ protected override void OnInitialized() var sourcePort = node1.AddPort(PortAlignment.Right); var targetPort = node2.AddPort(PortAlignment.Left); - diagram.Nodes.Add(node1, node2); + diagram.Nodes.Add(new[] { node1, node2 }); diagram.Links.Add(new LinkModel(sourcePort, targetPort)); } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs index 9e3bee89..88aaafd4 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs @@ -38,24 +38,24 @@ private void Diagram_GroupUngrouped(GroupModel obj) Console.WriteLine($"GroupUngrouped, Id={obj.Id}"); } - private void Links_Removed(BaseLinkModel[] obj) + private void Links_Removed(BaseLinkModel obj) { - Console.WriteLine($"Links.Removed, Links=[{string.Join(", ", obj.Select(x => x.Id))}]"); + Console.WriteLine($"Links.Removed, Links=[{obj}]"); } - private void Links_Added(BaseLinkModel[] obj) + private void Links_Added(BaseLinkModel obj) { - Console.WriteLine($"Links.Added, Links=[{string.Join(", ", obj.Select(x => x.Id))}]"); + Console.WriteLine($"Links.Added, Links=[{obj}]"); } - private void Nodes_Removed(NodeModel[] obj) + private void Nodes_Removed(NodeModel obj) { - Console.WriteLine($"Nodes.Removed, Nodes=[{string.Join(", ", obj.Select(x => x.Id))}]"); + Console.WriteLine($"Nodes.Removed, Nodes=[{obj}]"); } - private void Nodes_Added(NodeModel[] obj) + private void Nodes_Added(NodeModel obj) { - Console.WriteLine($"Nodes.Added, Nodes=[{string.Join(", ", obj.Select(x => x.Id))}]"); + Console.WriteLine($"Nodes.Added, Nodes=[{obj}]"); } private void Diagram_PanChanged() diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index fc28a58f..014e3471 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -1,5 +1,6 @@ using Blazor.Diagrams.Core.Behaviors; using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Layers; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; using Blazor.Diagrams.Core.Models.Core; @@ -13,7 +14,7 @@ [assembly: InternalsVisibleTo("Blazor.Diagrams")] namespace Blazor.Diagrams.Core { - public class Diagram + public class Diagram : Model { private readonly Dictionary _behaviors; private readonly Dictionary _componentByModelMapping; @@ -26,7 +27,6 @@ public class Diagram public event Action? Wheel; public event Action? MouseClick; - public event Action? Changed; public event Action? SelectionChanged; public event Action? GroupAdded; public event Action? GroupUngrouped; @@ -43,13 +43,8 @@ public Diagram(DiagramOptions? options = null) _groups = new List(); Options = options ?? new DiagramOptions(); - Nodes = new Layer(); - Links = new Layer(); - - Nodes.Added += OnNodesAdded; - Nodes.Removed += OnNodesRemoved; - Links.Added += OnLinksAdded; - Links.Removed += OnLinksRemoved; + Nodes = new NodeLayer(this); + Links = new LinkLayer(this); RegisterBehavior(new SelectionBehavior(this)); RegisterBehavior(new DragMovablesBehavior(this)); @@ -61,57 +56,36 @@ public Diagram(DiagramOptions? options = null) RegisterBehavior(new EventsBehavior(this)); } - public Layer Nodes { get; } - public Layer Links { get; } + public NodeLayer Nodes { get; } + public LinkLayer Links { get; } public IReadOnlyList Groups => _groups; public Rectangle? Container { get; internal set; } public Point Pan { get; internal set; } = Point.Zero; public double Zoom { get; private set; } = 1; public DiagramOptions Options { get; } + public bool SuspendRefresh { get; set; } - private void OnNodesAdded(NodeModel[] _) => Refresh(); - - private void OnNodesRemoved(NodeModel[] nodes) - { - foreach (var node in nodes) - { - Links.Remove(node.AllLinks.ToArray()); - } - - Refresh(); - } - - private void OnLinksAdded(BaseLinkModel[] links) + public override void Refresh() { - foreach (var link in links) - { - link.SourcePort.AddLink(link); - link.TargetPort?.AddLink(link); - - link.SourcePort.Refresh(); - link.TargetPort?.Refresh(); - - link.SourcePort.Parent.Group?.Refresh(); - link.TargetPort?.Parent.Group?.Refresh(); - } + if (SuspendRefresh) + return; - Refresh(); + base.Refresh(); } - private void OnLinksRemoved(BaseLinkModel[] links) + public void Batch(Action action) { - foreach (var link in links) + if (SuspendRefresh) { - link.SourcePort.RemoveLink(link); - link.TargetPort?.RemoveLink(link); - - link.SourcePort.Refresh(); - link.TargetPort?.Refresh(); - - link.SourcePort.Parent.Group?.Refresh(); - link.TargetPort?.Parent.Group?.Refresh(); + // If it's already suspended, just execute the action and leave it suspended + // It's probably handled by an outer batch + action(); + return; } + SuspendRefresh = true; + action(); + SuspendRefresh = false; Refresh(); } @@ -291,8 +265,6 @@ public void RegisterModelComponent(Type modelType, Type componentType) return _componentByModelMapping.ContainsKey(modelType) ? _componentByModelMapping[modelType] : null; } - public void Refresh() => Changed?.Invoke(); - public void ZoomToFit(double margin = 10) { if (Container == null || Nodes.Count == 0) diff --git a/src/Blazor.Diagrams.Core/Layer.cs b/src/Blazor.Diagrams.Core/Layer.cs deleted file mode 100644 index d5d1e638..00000000 --- a/src/Blazor.Diagrams.Core/Layer.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Blazor.Diagrams.Core.Models.Base; -using System; -using System.Collections; -using System.Collections.Generic; - -namespace Blazor.Diagrams.Core -{ - public class Layer : IReadOnlyList where T : Model - { - private readonly List _items = new List(); - - public event Action? Added; - public event Action? Removed; - - public void Add(params T[] items) - { - if (items.Length == 0) - return; - - _items.AddRange(items); - Added?.Invoke(items); - } - - public void Remove(params T[] items) - { - if (items.Length == 0) - return; - - foreach (var item in items) - { - _items.Remove(item); - } - - // May contain items not removed (because they weren't in the list) - // It's up to the user to only give existing items - Removed?.Invoke(items); - } - - public bool Contains(T item) => _items.Contains(item); - - public void Clear() - { - if (Count == 0) - return; - - var items = _items.ToArray(); - _items.Clear(); - Removed?.Invoke(items); - } - - public int Count => _items.Count; - public T this[int index] => _items[index]; - public IEnumerator GetEnumerator() => _items.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); - } -} diff --git a/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs b/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs new file mode 100644 index 00000000..bfdb6ff3 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs @@ -0,0 +1,108 @@ +using Blazor.Diagrams.Core.Models.Base; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Blazor.Diagrams.Core +{ + public abstract class BaseLayer : IReadOnlyList where T : Model + { + private readonly List _items = new List(); + + public event Action? Added; + public event Action? Removed; + + public BaseLayer(Diagram diagram) + { + Diagram = diagram; + } + + public virtual void Add(T item) + { + if (item is null) + throw new ArgumentNullException(nameof(item)); + + _items.Add(item); + OnItemAdded(item); + Added?.Invoke(item); + Diagram.Refresh(); + } + + public virtual void Add(IEnumerable items) + { + if (items is null) + throw new ArgumentNullException(nameof(items)); + + Diagram.Batch(() => + { + foreach (var item in items) + { + _items.Add(item); + OnItemAdded(item); + Added?.Invoke(item); + } + }); + } + + public virtual void Remove(T item) + { + if (item is null) + throw new ArgumentNullException(nameof(item)); + + if (_items.Remove(item)) + { + OnItemRemoved(item); + Removed?.Invoke(item); + Diagram.Refresh(); + } + } + + public virtual void Remove(IEnumerable items) + { + if (items is null) + throw new ArgumentNullException(nameof(items)); + + Diagram.Batch(() => + { + foreach (var item in items) + { + if (_items.Remove(item)) + { + OnItemRemoved(item); + Removed?.Invoke(item); + } + } + }); + } + + public bool Contains(T item) => _items.Contains(item); + + public void Clear() + { + if (Count == 0) + return; + + Diagram.Batch(() => + { + for (var i = _items.Count - 1; i >= 0; i--) + { + var item = _items[i]; + _items.RemoveAt(i); + OnItemRemoved(item); + Removed?.Invoke(item); + } + }); + } + + protected virtual void OnItemAdded(T item) { } + + protected virtual void OnItemRemoved(T item) { } + + public Diagram Diagram { get; } + + public int Count => _items.Count; + public T this[int index] => _items[index]; + public IEnumerator GetEnumerator() => _items.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); + } +} diff --git a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs new file mode 100644 index 00000000..ded1fe73 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs @@ -0,0 +1,33 @@ +using Blazor.Diagrams.Core.Models.Base; + +namespace Blazor.Diagrams.Core.Layers +{ + public class LinkLayer : BaseLayer + { + public LinkLayer(Diagram diagram) : base(diagram) { } + + protected override void OnItemAdded(BaseLinkModel link) + { + link.SourcePort.AddLink(link); + link.TargetPort?.AddLink(link); + + link.SourcePort.Refresh(); + link.TargetPort?.Refresh(); + + link.SourcePort.Parent.Group?.Refresh(); + link.TargetPort?.Parent.Group?.Refresh(); + } + + protected override void OnItemRemoved(BaseLinkModel link) + { + link.SourcePort.RemoveLink(link); + link.TargetPort?.RemoveLink(link); + + link.SourcePort.Refresh(); + link.TargetPort?.Refresh(); + + link.SourcePort.Parent.Group?.Refresh(); + link.TargetPort?.Parent.Group?.Refresh(); + } + } +} diff --git a/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs b/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs new file mode 100644 index 00000000..15f21330 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs @@ -0,0 +1,21 @@ +using Blazor.Diagrams.Core.Models; +using System; +using System.Linq; + +namespace Blazor.Diagrams.Core.Layers +{ + public class NodeLayer : BaseLayer + { + public NodeLayer(Diagram diagram) : base(diagram) { } + + public override void Remove(NodeModel node) + { + Diagram.Batch(() => base.Remove(node)); + } + + protected override void OnItemRemoved(NodeModel node) + { + Diagram.Links.Remove(node.AllLinks.ToList()); + } + } +} diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index fcbfaf0b..80e89307 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -87,7 +87,6 @@ public override void SetPosition(double x, double y) public virtual void UpdatePositionSilently(double deltaX, double deltaY) { base.SetPosition(Position.X + deltaX, Position.Y + deltaY); - UpdatePortPositions(deltaX, deltaY); Refresh(); } diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index 34853830..aaff83aa 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -45,7 +45,8 @@ public void RefreshAll() public T GetParent() where T : NodeModel => (T)Parent; - public virtual bool CanAttachTo(PortModel port) => port != this && !port.Locked && Parent != port.Parent; + public virtual bool CanAttachTo(PortModel port) + => port != this && !port.Locked && Parent != port.Parent; internal void AddLink(BaseLinkModel link) => _links.Add(link); diff --git a/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs index 2e60cc7f..4ee45743 100644 --- a/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs @@ -45,20 +45,14 @@ protected override void OnParametersSet() private void Diagram_Changed() => Refresh(); - private void Diagram_NodesAdded(NodeModel[] nodes) + private void Diagram_NodesAdded(NodeModel node) { - foreach (var node in nodes) - { - node.Changed += Refresh; - } + node.Changed += Refresh; } - private void Diagram_NodesRemoved(NodeModel[] nodes) + private void Diagram_NodesRemoved(NodeModel node) { - foreach (var node in nodes) - { - node.Changed -= Refresh; - } + node.Changed -= Refresh; } private void Diagram_GroupAdded(GroupModel group) => group.Changed += Refresh; From 2e6f29f8adfb24d16cc2df378ff21c16e64f7bb1 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Thu, 4 Mar 2021 10:25:04 +0100 Subject: [PATCH 37/67] fix: SetContainer is now public --- src/Blazor.Diagrams.Core/Diagram.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index 014e3471..f4a47fcb 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -301,7 +301,7 @@ public void SetZoom(double newZoom) Refresh(); } - internal void SetContainer(Rectangle newRect) + public void SetContainer(Rectangle newRect) { Container = newRect; ContainerChanged?.Invoke(); From 667ab2d22e2fc194adec01ec50aaac2ae73dfd72 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 5 Mar 2021 06:26:44 +0100 Subject: [PATCH 38/67] fix: Deleted nodes from groups would still show them --- src/Blazor.Diagrams.Core/Layers/NodeLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs b/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs index 15f21330..c8b041b8 100644 --- a/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs +++ b/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs @@ -1,5 +1,4 @@ using Blazor.Diagrams.Core.Models; -using System; using System.Linq; namespace Blazor.Diagrams.Core.Layers @@ -16,6 +15,7 @@ public override void Remove(NodeModel node) protected override void OnItemRemoved(NodeModel node) { Diagram.Links.Remove(node.AllLinks.ToList()); + node.Group?.RemoveChild(node); } } } From e359f5acabd890f7e7e49b620b2349a6e3b105d4 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 6 Mar 2021 01:52:50 +0100 Subject: [PATCH 39/67] fix: NavigatorWidget not handling negative node positions + Add FillColor option --- .../Components/NavigatorWidget.razor | 19 ++++++++------- .../Components/NavigatorWidget.razor.cs | 23 +++++++----------- .../wwwroot/default.styles.css | 4 --- .../wwwroot/default.styles.min.css | 2 +- .../wwwroot/default.styles.min.css.gz | Bin 657 -> 636 bytes 5 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/Blazor.Diagrams/Components/NavigatorWidget.razor b/src/Blazor.Diagrams/Components/NavigatorWidget.razor index 57ff9f1b..7bfd1db1 100644 --- a/src/Blazor.Diagrams/Components/NavigatorWidget.razor +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor @@ -1,9 +1,9 @@ @if (Diagram.Container != null) { - var addedNodeX = Math.Max(0, Diagram.Pan.X); - var addedNodeY = Math.Max(0, Diagram.Pan.Y); - var addedCurrentViewX = Math.Abs(Math.Min(0, Diagram.Pan.X)) * XFactor; - var addedCurrentViewY = Math.Abs(Math.Min(0, Diagram.Pan.Y)) * YFactor; + var addedNodeX = Math.Max(0, Diagram.Pan.X) + NodePositionAdjustment.X; + var addedNodeY = Math.Max(0, Diagram.Pan.Y) + NodePositionAdjustment.Y; + var addedCurrentViewX = (Math.Abs(Math.Min(0, Diagram.Pan.X)) + NodePositionAdjustment.X) * XFactor; + var addedCurrentViewY = (Math.Abs(Math.Min(0, Diagram.Pan.Y)) + NodePositionAdjustment.Y) * YFactor; var currentViewWidth = Diagram.Container.Width * XFactor; var currentViewHeight = Diagram.Container.Height * YFactor; @@ -13,18 +13,19 @@ style="top: @(addedCurrentViewY.ToInvariantString())px; left: @(addedCurrentViewX.ToInvariantString())px; width: @(currentViewWidth.ToInvariantString())px; - height: @(currentViewHeight.ToInvariantString())px;"> + height: @(currentViewHeight.ToInvariantString())px; + border: 2px solid @FillColor;"> @foreach (var node in Diagram.Nodes.Where(n => n.Size != null)) { - var left = (Math.Max(0, node.Position.X * Diagram.Zoom) + addedNodeX) * XFactor; - var top = (Math.Max(0, node.Position.Y * Diagram.Zoom) + addedNodeY) * YFactor; + var left = ((node.Position.X * Diagram.Zoom) + addedNodeX) * XFactor; + var top = ((node.Position.Y * Diagram.Zoom) + addedNodeY) * YFactor; var width = node.Size.Width * Diagram.Zoom * XFactor; var height = node.Size.Height * Diagram.Zoom * YFactor; - @@ -38,7 +39,7 @@ var height = group.Size.Height * Diagram.Zoom * YFactor; - jj+-zLy-K90(ng9AA;~stPGj%@Zj6m=lVmsAll4cB z)GO3B5XeGE`=hb|dp@4u*fYcLpT7bM8L7EJLQ=S%88rpogkg1w3t)L+@WIU!sVFF< z2xYaldM6_3F;}t{6s3|&g_qghjn)=#s>?!4&gfzZ2RCALhUCZ$O5_FUf>3$J;UcnT z&J?+q$`kSuuH6Wt)jISV_k?$<&92jJ$GT``8T{uBDyf-aQeXv~7$?LXoaF+iAPlIu^SGD#1!4wDeKQ;&RQ5tf07K!AS{s(V+v~DRVLI{W zT?zymIHU&Xb$pMn@RrftBZoy$NoD@lt+8HrvcRQM)j~8`7!8up&r70mCNSEJGjbn( z`#s$m#T?iM7{ z7+FpOHadIa?t{aaLuUL@%pf_NIW(vLf;h`ex%-&|^aeemUp|5~vYLjnimQE|#%OIG z2iPiyK#zIM#MNnApxKBXv+-0~5)SBLOfyUx8XbWF3M089B@*Psa$=eg-*jw9*P|8H!ROv(+W|`&WdEa=*+SpP69QF9F1bSz@Okdsj z>e=_F35Z*#ab>pfZ^TIg{Mz+hCY%pSxJ-}nfZv#Pi*+Av5bYI|w}9KFfs46s)tPOv zNV5;X!Qs2N={j|-Em{);7E*w1)$(IXs>i~Spkx9y&2l^3!zdzjCRK_3%>JU_1=-A4 WyVuHNKQVct{`><@%vDc02mk<(Av8$< literal 657 zcmV;C0&e{uiwFP!000003bj>jj@uv*y-K90(nbmv`PydjX|NePG#D(vNp_<>S%36M zy+Q%ov9orZ_D6;6n0b%K49rY^|NIp($Y>=jl8V9g%$Yrq6~^TyJ^;@TmhAm^qBH}I zG-bRp4)-RS9&@cK$xy0<(qx|P{AjpjRbFOB3C?D7*!vNqGbBfDQKBx$94J#~f@f<- z^Hk(tI#1{eUiuNO7fT!v|KK}lwQaG@5L+*t2mg74QW2&bToSYzX5sd@a1;-qKV-_Mj_Aasgh`bg5Ma$L#*SNA-P(QzIJ z*noYZ$9_!2)uN5iWJHg>39d9wc%g?eO?Xnf(K^sm;WXE@K$4z#POZ}I>gzDao0|zm zRV(%Ci)^~0c}wU%!C(Ihc8*(8#< Date: Tue, 9 Mar 2021 18:02:53 +0100 Subject: [PATCH 40/67] feat: Orthogonal router --- src/Blazor.Diagrams.Core/Delegates.cs | 2 +- src/Blazor.Diagrams.Core/Models/Core/Point.cs | 6 + .../Models/Core/Rectangle.cs | 37 +- src/Blazor.Diagrams.Core/Models/NodeModel.cs | 23 +- .../Routers/Routers.Normal.cs | 10 +- .../Routers/Routers.Orthogonal.cs | 570 ++++++++++++++++++ .../Routers/Routers.Utils.cs | 32 + .../Components/LinkWidget.razor | 4 +- .../Components/LinkWidget.razor.cs | 28 - 9 files changed, 675 insertions(+), 37 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs create mode 100644 src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs index f6340085..9fbb9d56 100644 --- a/src/Blazor.Diagrams.Core/Delegates.cs +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -4,7 +4,7 @@ namespace Blazor.Diagrams.Core { - public delegate Point[] Router(Diagram diagram, BaseLinkModel link, Point from, Point to); + public delegate Point[] Router(Diagram diagram, BaseLinkModel link); public delegate PathGeneratorResult PathGenerator(Diagram diagram, BaseLinkModel link, Point[] route); diff --git a/src/Blazor.Diagrams.Core/Models/Core/Point.cs b/src/Blazor.Diagrams.Core/Models/Core/Point.cs index 64c099a1..50205b5c 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Point.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Point.cs @@ -29,5 +29,11 @@ public double DistanceTo(Point other) public static Point operator -(Point a, Point b) => new Point(a.X - b.X, a.Y - b.Y); public static Point operator +(Point a, Point b) => new Point(a.X + b.X, a.Y + b.Y); + + public void Deconstruct(out double x, out double y) + { + x = X; + y = Y; + } } } diff --git a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs b/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs index 3c80e4a6..081e1a94 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs +++ b/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs @@ -41,12 +41,47 @@ public Rectangle(Point position, Size size) public bool Overlap(Rectangle r) => Left < r.Right && Right > r.Left && Top < r.Bottom && Bottom > r.Top; + public bool Intersects(Rectangle r) + { + var thisX = Left; + var thisY = Top; + var thisW = Width; + var thisH = Height; + var rectX = r.Left; + var rectY = r.Top; + var rectW = r.Width; + var rectH = r.Height; + return (rectX < thisX + thisW) && (thisX < (rectX + rectW)) && (rectY < thisY + thisH) && (thisY < rectY + rectH); + } + + public Rectangle Inflate(double horizontal, double vertical) + => new Rectangle(Left - horizontal, Top - vertical, Right + horizontal, Bottom + vertical); + + public Rectangle Union(Rectangle r) + { + var x1 = Math.Min(Left, r.Left); + var x2 = Math.Max(Left + Width, r.Left + r.Width); + var y1 = Math.Min(Top, r.Top); + var y2 = Math.Max(Top + Height, r.Top + r.Height); + return new Rectangle(x1, y1, x2, y2); + } + public bool ContainsPoint(Point point) => ContainsPoint(point.X, point.Y); public bool ContainsPoint(double x, double y) => x >= Left && x <= Right && y >= Top && y <= Bottom; + public Point Center => new Point(Left + Width / 2, Top + Height / 2); + public Point NorthEast => new Point(Right, Top); + public Point SouthEast => new Point(Right, Bottom); + public Point SouthWest => new Point(Left, Bottom); + public Point NorthWest => new Point(Left, Top); + public Point East => new Point(Right, Top + Height / 2); + public Point North => new Point(Left + Width / 2, Top); + public Point South => new Point(Left + Width / 2, Bottom); + public Point West => new Point(Left, Top + Height / 2); + public override string ToString() - => $"Rectangle(width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})"; + => $"Rectangle(width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})"; } } diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index 80e89307..78c3da1b 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -91,7 +91,28 @@ public virtual void UpdatePositionSilently(double deltaX, double deltaY) Refresh(); } - public Rectangle? GetBounds() => Size == null ? null : new Rectangle(Position, Size); + public Rectangle? GetBounds(bool includePorts = false) + { + if (Size == null) + return null; + + if (!includePorts) + return new Rectangle(Position, Size); + + var leftPort = GetPort(PortAlignment.Left); + var topPort = GetPort(PortAlignment.Top); + var rightPort = GetPort(PortAlignment.Right); + var bottomPort = GetPort(PortAlignment.Bottom); + + var left = leftPort == null ? Position.X : Math.Min(Position.X, leftPort.Position.X); + var top = topPort == null ? Position.Y : Math.Min(Position.Y, topPort.Position.Y); + var right = rightPort == null ? Position.X + Size!.Width : + Math.Max(rightPort.Position.X + rightPort.Size.Width, Position.X + Size!.Width); + var bottom = bottomPort == null ? Position.Y + Size!.Height : + Math.Max(bottomPort.Position.Y + bottomPort.Size.Height, Position.Y + Size!.Height); + + return new Rectangle(left, top, right, bottom); + } private void UpdatePortPositions(double deltaX, double deltaY) { diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs index fba82035..f0f7c824 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs @@ -7,15 +7,19 @@ namespace Blazor.Diagrams.Core { public static partial class Routers { - public static Point[] Normal(Diagram _, BaseLinkModel link, Point from, Point to) + public static Point[] Normal(Diagram _, BaseLinkModel link) { var route = new Point[link.Vertices.Count + 2]; - route[0] = from; + route[0] = link.SourceMarker == null ? link.SourcePort.MiddlePosition : GetPortPositionBasedOnAlignment(link.SourcePort); + if (link.Vertices.Count > 0) { Array.Copy(link.Vertices.Select(v => v.Position).ToArray(), 0, route, 1, link.Vertices.Count); } - route[^1] = to; + + route[^1] = link.TargetPort == null + ? link.OnGoingPosition! + : link.TargetMarker == null ? link.TargetPort.MiddlePosition : GetPortPositionBasedOnAlignment(link.TargetPort); return route; } } diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs new file mode 100644 index 00000000..9317522b --- /dev/null +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs @@ -0,0 +1,570 @@ +using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; +using Blazor.Diagrams.Core.Models.Core; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; + +// Implementation taken from the JS version: https://gist.github.com/menendezpoo/4a8894c152383b9d7a870c24a04447e4 +// Todo: Make it more c#, Benchmark A* vs Dijkstra, Add more options +namespace Blazor.Diagrams.Core +{ + public static partial class Routers + { + public static Point[] Orthogonal(Diagram _, BaseLinkModel link) + { + if (link.TargetPort == null || link.SourcePort.Parent.Size == null || link.TargetPort.Parent.Size == null) + return Normal(_, link); + + var shapeMargin = 10; + var globalBoundsMargin = 50; + var spots = new List(); + var verticals = new List(); + var horizontals = new List(); + var sideA = link.SourcePort.Alignment; + var sideAVertical = IsVerticalSide(sideA); + var sideB = link.TargetPort.Alignment; + var sideBVertical = IsVerticalSide(sideB); + var originA = GetPortPositionBasedOnAlignment(link.SourcePort); + var originB = GetPortPositionBasedOnAlignment(link.TargetPort); + var shapeA = link.SourcePort.Parent.GetBounds(includePorts: true); + var shapeB = link.TargetPort.Parent.GetBounds(includePorts: true); + var inflatedA = shapeA.Inflate(shapeMargin, shapeMargin); + var inflatedB = shapeB.Inflate(shapeMargin, shapeMargin); + + if (inflatedA.Intersects(inflatedB)) + { + shapeMargin = 0; + inflatedA = shapeA; + inflatedB = shapeB; + } + + // Curated bounds to stick to + var bounds = inflatedA.Union(inflatedB).Inflate(globalBoundsMargin, globalBoundsMargin); + + // Add edges to rulers + verticals.Add(inflatedA.Left); + verticals.Add(inflatedA.Right); + horizontals.Add(inflatedA.Top); + horizontals.Add(inflatedA.Bottom); + verticals.Add(inflatedB.Left); + verticals.Add(inflatedB.Right); + horizontals.Add(inflatedB.Top); + horizontals.Add(inflatedB.Bottom); + + // Rulers at origins of shapes + (sideAVertical ? verticals : horizontals).Add(sideAVertical ? originA.X : originA.Y); + (sideBVertical ? verticals : horizontals).Add(sideBVertical ? originB.X : originB.Y); + + // Points of shape antennas + spots.Add(GetOriginSpot(originA, sideA, shapeMargin)); + spots.Add(GetOriginSpot(originB, sideB, shapeMargin)); + + // Sort rulers + verticals.Sort(); + horizontals.Sort(); + + // Create grid + var grid = RulersToGrid(verticals, horizontals, bounds); + var gridPoints = GridToSpots(grid, new[] { inflatedA, inflatedB }); + + // Add to spots + spots.AddRange(gridPoints); + + // Create graph + var graph = CreateGraph(spots); + + // Origin and destination by extruding antennas + var origin = ExtrudeCp(originA, shapeMargin, sideA); + var destination = ExtrudeCp(originB, shapeMargin, sideB); + + var start = originA; + var end = originB; + + var path = ShortestPath(graph, origin, destination); + if (path.Length > 0) + { + var result = new List(); + result.Add(start); + result.AddRange(SimplifyPath(path)); + result.Add(end); + return result.ToArray(); + } + else + { + return Normal(_, link); + } + } + + private static Point GetOriginSpot(Point p, PortAlignment alignment, double shapeMargin) + { + return alignment switch + { + PortAlignment.Top => p.Add(0, -shapeMargin), + PortAlignment.Right => p.Add(shapeMargin, 0), + PortAlignment.Bottom => p.Add(0, shapeMargin), + PortAlignment.Left => p.Add(-shapeMargin, 0), + _ => throw new NotImplementedException() + }; + } + + private static bool IsVerticalSide(PortAlignment alignment) + => alignment == PortAlignment.Top || alignment == PortAlignment.Bottom; // Add others + + private static Grid RulersToGrid(List verticals, List horizontals, Rectangle bounds) + { + var result = new Grid(); + verticals.Sort(); + horizontals.Sort(); + + var lastX = bounds.Left; + var lastY = bounds.Top; + var column = 0; + var row = 0; + + foreach (var y in horizontals) + { + foreach (var x in verticals) + { + result.Set(row, column++, new Rectangle(lastX, lastY, x, y)); + lastX = x; + } + + // Last cell of the row + result.Set(row, column, new Rectangle(lastX, lastY, bounds.Right, y)); + lastX = bounds.Left; + lastY = y; + column = 0; + row++; + } + + lastX = bounds.Left; + + // Last fow of cells + foreach (var x in verticals) + { + result.Set(row, column++, new Rectangle(lastX, lastY, x, bounds.Bottom)); + lastX = x; + } + + // Last cell of last row + result.Set(row, column, new Rectangle(lastX, lastY, bounds.Right, bounds.Bottom)); + return result; + } + + private static List GridToSpots(Grid grid, Rectangle[] obstacles) + { + bool obstacleCollision(Point p) => obstacles.Where(o => o.ContainsPoint(p)).Any(); + + var gridPoints = new List(); + foreach (var (row, data) in grid.Data) + { + + var firstRow = row == 0; + var lastRow = row == grid.Rows - 1; + + foreach (var (col, r) in data) + { + + var firstCol = col == 0; + var lastCol = col == grid.Columns - 1; + var nw = firstCol && firstRow; + var ne = firstRow && lastCol; + var se = lastRow && lastCol; + var sw = lastRow && firstCol; + + if (nw || ne || se || sw) + { + gridPoints.Add(r.NorthWest); + gridPoints.Add(r.NorthEast); + gridPoints.Add(r.SouthWest); + gridPoints.Add(r.SouthEast); + } + else if (firstRow) + { + gridPoints.Add(r.NorthWest); + gridPoints.Add(r.North); + gridPoints.Add(r.NorthEast); + } + else if (lastRow) + { + gridPoints.Add(r.SouthEast); + gridPoints.Add(r.South); + gridPoints.Add(r.SouthWest); + } + else if (firstCol) + { + gridPoints.Add(r.NorthWest); + gridPoints.Add(r.West); + gridPoints.Add(r.SouthWest); + } + else if (lastCol) + { + gridPoints.Add(r.NorthEast); + gridPoints.Add(r.East); + gridPoints.Add(r.SouthEast); + } + else + { + gridPoints.Add(r.NorthWest); + gridPoints.Add(r.North); + gridPoints.Add(r.NorthEast); + gridPoints.Add(r.East); + gridPoints.Add(r.SouthEast); + gridPoints.Add(r.South); + gridPoints.Add(r.SouthWest); + gridPoints.Add(r.West); + gridPoints.Add(r.Center); + } + } + } + + // Reduce repeated points and filter out those who touch shapes + return ReducePoints(gridPoints).Where(p => !obstacleCollision(p)).ToList(); + } + + private static IEnumerable ReducePoints(List points) + { + var map = new Dictionary>(); + foreach (var p in points) + { + (var x, var y) = p; + if (!map.ContainsKey(y)) map.Add(y, new List()); + var arr = map[y]; + + if (!arr.Contains(x)) arr.Add(x); + } + + foreach (var (y, xs) in map) + { + foreach (var x in xs) + { + yield return new Point(x, y); + } + } + } + + private static PointGraph CreateGraph(List spots) + { + var hotXs = new List(); + var hotYs = new List(); + var graph = new PointGraph(); + + spots.ForEach(p => + { + (var x, var y) = p; + if (!hotXs.Contains(x)) hotXs.Add(x); + if (!hotYs.Contains(y)) hotYs.Add(y); + graph.Add(p); + }); + + hotXs.Sort(); + hotYs.Sort(); + + for (var i = 0; i < hotYs.Count; i++) + { + for (var j = 0; j < hotXs.Count; j++) + { + var b = new Point(hotXs[j], hotYs[i]); + if (!graph.Has(b)) continue; + + if (j > 0) + { + var a = new Point(hotXs[j - 1], hotYs[i]); + + if (graph.Has(a)) + { + graph.Connect(a, b); + graph.Connect(b, a); + } + } + + if (i > 0) + { + var a = new Point(hotXs[j], hotYs[i - 1]); + + if (graph.Has(a)) + { + graph.Connect(a, b); + graph.Connect(b, a); + } + } + } + } + + return graph; + } + + private static Point ExtrudeCp(Point p, double margin, PortAlignment alignment) + { + return alignment switch + { + PortAlignment.Top => p.Add(0, -margin), + PortAlignment.Right => p.Add(margin, 0), + PortAlignment.Bottom => p.Add(0, margin), + PortAlignment.Left => p.Add(-margin, 0), + _ => throw new NotImplementedException(), + }; + } + + private static Point[] ShortestPath(PointGraph graph, Point origin, Point destination) + { + var originNode = graph.Get(origin); + var destinationNode = graph.Get(destination); + + if (originNode == null || destinationNode == null) + throw new Exception("Origin node or Destination node not found"); + + graph.CalculateShortestPathFromSource(graph, originNode); + return destinationNode.ShortestPath.Select(n => n.Data).ToArray(); + } + + private static Point[] SimplifyPath(Point[] points) + { + if (points.Length <= 2) + { + return points; + } + + var r = new List() { points[0] }; + for (var i = 1; i < points.Length; i++) + { + var cur = points[i]; + if (i == (points.Length - 1)) + { + r.Add(cur); + break; + } + + var prev = points[i - 1]; + var next = points[i + 1]; + var bend = GetBend(prev, cur, next); + + if (bend != "none") + { + r.Add(cur); + } + } + + return r.ToArray(); + } + + private static string GetBend(Point a, Point b, Point c) + { + var equalX = a.X == b.X && b.X == c.X; + var equalY = a.Y == b.Y && b.Y == c.Y; + var segment1Horizontal = a.Y == b.Y; + var segment1Vertical = a.X == b.X; + var segment2Horizontal = b.Y == c.Y; + var segment2Vertical = b.X == c.X; + + if (equalX || equalY) + { + return "none"; + } + + if ( + !(segment1Vertical || segment1Horizontal) || + !(segment2Vertical || segment2Horizontal) + ) + { + return "unknown"; + } + + if (segment1Horizontal && segment2Vertical) + { + return c.Y > b.Y ? "s" : "n"; + + } + else if (segment1Vertical && segment2Horizontal) + { + return c.X > b.X ? "e" : "w"; + } + + throw new Exception("Nope"); + } + } + + class Grid + { + public Grid() + { + Data = new Dictionary>(); + } + + public Dictionary> Data { get; } + public double Rows { get; private set; } + public double Columns { get; private set; } + + public void Set(double row, double column, Rectangle rectangle) + { + Rows = Math.Max(Rows, row + 1); + Columns = Math.Max(Columns, column + 1); + + if (!Data.ContainsKey(row)) + { + Data.Add(row, new Dictionary()); + } + + Data[row].Add(column, rectangle); + } + + public Rectangle? Get(double row, double column) + { + if (!Data.ContainsKey(row)) + return null; + + if (!Data[row].ContainsKey(column)) + return null; + + return Data[row][column]; + } + + public Rectangle[] Rectangles() => Data.SelectMany(r => r.Value.Values).ToArray(); + } + + class PointGraph + { + public readonly Dictionary> _index = new Dictionary>(); + + public void Add(Point p) + { + (var x, var y) = p; + var xs = x.ToInvariantString(); + var ys = y.ToInvariantString(); + + if (!_index.ContainsKey(xs)) + _index.Add(xs, new Dictionary()); + + if (!_index[xs].ContainsKey(ys)) + _index[xs].Add(ys, new PointNode(p)); + } + + private PointNode GetLowestDistanceNode(HashSet unsettledNodes) + { + PointNode? lowestDistanceNode = null; + var lowestDistance = double.MaxValue; + foreach (var node in unsettledNodes) + { + var nodeDistance = node.Distance; + if (nodeDistance < lowestDistance) + { + lowestDistance = nodeDistance; + lowestDistanceNode = node; + } + } + + return lowestDistanceNode!; + } + + public PointGraph CalculateShortestPathFromSource(PointGraph graph, PointNode source) + { + source.Distance = 0; + var settledNodes = new HashSet(); + var unsettledNodes = new HashSet + { + source + }; + + while (unsettledNodes.Count != 0) + { + var currentNode = GetLowestDistanceNode(unsettledNodes); + unsettledNodes.Remove(currentNode); + + foreach ((var adjacentNode, var edgeWeight) in currentNode.AdjacentNodes) + { + if (!settledNodes.Contains(adjacentNode)) + { + CalculateMinimumDistance(adjacentNode, edgeWeight, currentNode); + unsettledNodes.Add(adjacentNode); + } + + } + settledNodes.Add(currentNode); + } + + return graph; + } + + private void CalculateMinimumDistance(PointNode evaluationNode, double edgeWeight, PointNode sourceNode) + { + var sourceDistance = sourceNode.Distance; + var comingDirection = InferPathDirection(sourceNode); + var goingDirection = DirectionOfNodes(sourceNode, evaluationNode); + var changingDirection = comingDirection != null && goingDirection != null && comingDirection != goingDirection; + var extraWeigh = changingDirection ? Math.Pow(edgeWeight + 1, 2) : 0; + + if (sourceDistance + edgeWeight + extraWeigh < evaluationNode.Distance) + { + evaluationNode.Distance = sourceDistance + edgeWeight + extraWeigh; + var shortestPath = new List(); + shortestPath.AddRange(sourceNode.ShortestPath); + shortestPath.Add(sourceNode); + evaluationNode.ShortestPath = shortestPath; + } + } + + private char? DirectionOf(Point a, Point b) + { + if (a.X == b.X) return 'h'; + else if (a.Y == b.Y) return 'v'; + return null; + } + + private char? DirectionOfNodes(PointNode a, PointNode b) => DirectionOf(a.Data, b.Data); + + private char? InferPathDirection(PointNode node) + { + if (node.ShortestPath.Count == 0) + return null; + + return DirectionOfNodes(node.ShortestPath[node.ShortestPath.Count - 1], node); + } + + public void Connect(Point a, Point b) + { + var nodeA = Get(a); + var nodeB = Get(b); + + if (nodeA == null || nodeB == null) + return; + + nodeA.AdjacentNodes.Add(nodeB, a.DistanceTo(b)); + } + + public bool Has(Point p) + { + (var x, var y) = p; + var xs = x.ToInvariantString(); + var ys = y.ToInvariantString(); + return _index.ContainsKey(xs) && _index[xs].ContainsKey(ys); + } + + public PointNode? Get(Point p) + { + (var x, var y) = p; + var xs = x.ToInvariantString(); + var ys = y.ToInvariantString(); + + if (_index.ContainsKey(xs) && _index[xs].ContainsKey(ys)) + return _index[xs][ys]; + + return null; + } + } + + class PointNode + { + public PointNode(Point data) + { + Data = data; + } + + public Point Data { get; } + public double Distance { get; set; } = double.MaxValue; + public List ShortestPath { get; set; } = new List(); + public Dictionary AdjacentNodes { get; set; } = new Dictionary(); + } +} diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs new file mode 100644 index 00000000..6e344bc8 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs @@ -0,0 +1,32 @@ +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Core; + +namespace Blazor.Diagrams.Core +{ + public static partial class Routers + { + public static Point GetPortPositionBasedOnAlignment(PortModel port) + { + var pt = port.Position; + switch (port.Alignment) + { + case PortAlignment.Top: + return new Point(pt.X + port.Size.Width / 2, pt.Y); + case PortAlignment.TopRight: + return new Point(pt.X + port.Size.Width, pt.Y); + case PortAlignment.Right: + return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height / 2); + case PortAlignment.BottomRight: + return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height); + case PortAlignment.Bottom: + return new Point(pt.X + port.Size.Width / 2, pt.Y + port.Size.Height); + case PortAlignment.BottomLeft: + return new Point(pt.X, pt.Y + port.Size.Height); + case PortAlignment.Left: + return new Point(pt.X, pt.Y + port.Size.Height / 2); + default: + return pt; + } + } + } +} diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index aacca108..e702a155 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -2,9 +2,7 @@ @{ var router = Link.Router ?? Diagram.Options.Links.DefaultRouter; var pathGenerator = Link.PathGenerator ?? Diagram.Options.Links.DefaultPathGenerator; - var sourcePosition = GetPositionBasedOnAlignment(Link.SourcePort, Link.SourceMarker); - var targetPosition = Link.IsAttached ? GetPositionBasedOnAlignment(Link.TargetPort, Link.TargetMarker) : Link.OnGoingPosition; - var route = router(Diagram, Link, sourcePosition, targetPosition); + var route = router(Diagram, Link); var result = pathGenerator(Diagram, Link, route); var color = Link.Selected ? (Link.SelectedColor ?? Diagram.Options.Links.DefaultSelectedColor) : (Link.Color ?? Diagram.Options.Links.DefaultColor); diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index e86908a3..7594f149 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Components; using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; -using System; namespace Blazor.Diagrams.Components { @@ -15,33 +14,6 @@ public partial class LinkWidget [Parameter] public LinkModel Link { get; set; } - private Point GetPositionBasedOnAlignment(PortModel port, LinkMarker marker) - { - if (marker == null) - return port.MiddlePosition; - - var pt = port.Position; - switch (port.Alignment) - { - case PortAlignment.Top: - return new Point(pt.X + port.Size.Width / 2, pt.Y); - case PortAlignment.TopRight: - return new Point(pt.X + port.Size.Width, pt.Y); - case PortAlignment.Right: - return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height / 2); - case PortAlignment.BottomRight: - return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height); - case PortAlignment.Bottom: - return new Point(pt.X + port.Size.Width / 2, pt.Y + port.Size.Height); - case PortAlignment.BottomLeft: - return new Point(pt.X, pt.Y + port.Size.Height); - case PortAlignment.Left: - return new Point(pt.X, pt.Y + port.Size.Height / 2); - default: - return pt; - } - } - private void OnMouseDown(MouseEventArgs e, int index) { if (!Link.Segmentable) From 5bd552c19f3f14e58aad83e2ecdbae3cf26cf8b4 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Tue, 9 Mar 2021 18:04:35 +0100 Subject: [PATCH 41/67] fix: Disallow panning with right click --- src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs index 880a304d..b5448943 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs @@ -20,7 +20,7 @@ public PanBehavior(Diagram diagram) : base(diagram) private void Diagram_MouseDown(Model model, MouseEventArgs e) { - if (!Diagram.Options.AllowPanning || model != null || e.ShiftKey) + if (!Diagram.Options.AllowPanning || model != null || e.ShiftKey || e.Button != (int)MouseEventButton.Left) return; _initialPan = Diagram.Pan; From 3c96748696a4873de2ad2e4e696635611fc7c443 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 12 Mar 2021 19:08:52 +0100 Subject: [PATCH 42/67] feat: Links between nodes directly (without ports) Move core models to Geometry namespace --- .../ReconnectLinksToClosestPorts.razor.cs | 2 +- samples/SharedDemo/Demos/BotAnswerNode.cs | 4 +- .../Demos/CustomGroup/Demo.razor.cs | 2 +- .../SharedDemo/Demos/CustomLink/Demo.razor.cs | 2 +- samples/SharedDemo/Demos/CustomNode.razor.cs | 2 +- .../SharedDemo/Demos/CustomPort/Demo.razor.cs | 2 +- .../Demos/DynamicInsertions.razor.cs | 2 +- samples/SharedDemo/Demos/Events.razor.cs | 2 +- samples/SharedDemo/Demos/Grouping.razor.cs | 2 +- samples/SharedDemo/Demos/Locked.razor.cs | 2 +- samples/SharedDemo/Demos/Performance.razor.cs | 2 +- samples/SharedDemo/Demos/Simple.razor.cs | 8 ++- samples/SharedDemo/Demos/SnapToGrid.razor.cs | 2 +- samples/SharedDemo/Demos/ZoomToFit.razor.cs | 2 +- .../Extensions/PointExtensions.cs | 2 +- .../Behaviors/DragMovablesBehavior.cs | 4 +- .../Behaviors/DragNewLinkBehavior.cs | 12 ++--- .../Behaviors/PanBehavior.cs | 4 +- .../Behaviors/ZoomBehavior.cs | 2 +- src/Blazor.Diagrams.Core/Delegates.cs | 6 +-- src/Blazor.Diagrams.Core/Diagram.cs | 2 +- .../Extensions/BaseLinkModelExtensions.cs | 1 + .../Extensions/DiagramExtensions.cs | 4 +- .../Geometry/BezierSpline.cs | 3 +- src/Blazor.Diagrams.Core/Geometry/IShape.cs | 9 ++++ src/Blazor.Diagrams.Core/Geometry/Line.cs | 43 ++++++++++++++++ .../{Models/Core => Geometry}/Point.cs | 2 +- .../{Models/Core => Geometry}/Rectangle.cs | 24 +++++++-- src/Blazor.Diagrams.Core/Geometry/Shapes.cs | 11 +++++ .../{Models/Core => Geometry}/Size.cs | 2 +- src/Blazor.Diagrams.Core/Layers/LinkLayer.cs | 40 ++++++++++----- .../Models/Base/BaseLinkModel.cs | 29 +++++++++-- .../Models/Base/MovableModel.cs | 2 +- src/Blazor.Diagrams.Core/Models/GroupModel.cs | 3 +- .../Models/LinkLabelModel.cs | 4 +- src/Blazor.Diagrams.Core/Models/LinkModel.cs | 4 ++ .../Models/LinkVertexModel.cs | 4 +- src/Blazor.Diagrams.Core/Models/NodeModel.cs | 32 +++++++++--- src/Blazor.Diagrams.Core/Models/PortModel.cs | 4 +- .../PathGenerators/PathGeneratorResult.cs | 2 +- .../PathGenerators/PathGenerators.Smooth.cs | 31 +++++++++--- .../PathGenerators/PathGenerators.Straight.cs | 8 +-- .../PathGenerators/PathGenerators.Utils.cs | 11 ++++- .../Routers/Routers.Normal.cs | 18 ++----- .../Routers/Routers.Orthogonal.cs | 11 +++-- .../Routers/Routers.Utils.cs | 4 +- .../Components/DiagramCanvas.razor.cs | 2 +- .../Components/Groups/GroupContainer.razor.cs | 2 +- .../Components/LinkWidget.razor | 4 +- .../Components/LinkWidget.razor.cs | 49 ++++++++++++++++++- .../Components/NavigatorWidget.razor.cs | 2 +- .../Components/Renderers/LinkLabelRenderer.cs | 2 +- .../Components/Renderers/NodeRenderer.cs | 3 +- .../Renderers/PortRenderer.razor.cs | 2 +- .../Components/SelectionBoxWidget.razor.cs | 2 +- .../Extensions/JSRuntimeExtensions.cs | 2 +- src/Blazor.Diagrams/_Imports.razor | 2 +- 57 files changed, 330 insertions(+), 116 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Geometry/IShape.cs create mode 100644 src/Blazor.Diagrams.Core/Geometry/Line.cs rename src/Blazor.Diagrams.Core/{Models/Core => Geometry}/Point.cs (96%) rename src/Blazor.Diagrams.Core/{Models/Core => Geometry}/Rectangle.cs (78%) create mode 100644 src/Blazor.Diagrams.Core/Geometry/Shapes.cs rename src/Blazor.Diagrams.Core/{Models/Core => Geometry}/Size.cs (92%) diff --git a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs index 8e3e0c9c..12c43ee8 100644 --- a/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs +++ b/samples/SharedDemo/Demos/Algorithms/ReconnectLinksToClosestPorts.razor.cs @@ -1,8 +1,8 @@ using Blazor.Diagrams.Algorithms; using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; +using Blazor.Diagrams.Core.Geometry; namespace SharedDemo.Demos.Algorithms { diff --git a/samples/SharedDemo/Demos/BotAnswerNode.cs b/samples/SharedDemo/Demos/BotAnswerNode.cs index 6083e45d..b345facb 100644 --- a/samples/SharedDemo/Demos/BotAnswerNode.cs +++ b/samples/SharedDemo/Demos/BotAnswerNode.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; namespace SharedDemo.Demos { diff --git a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs index 0b4c374b..79addca7 100644 --- a/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomGroup/Demo.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; namespace SharedDemo.Demos.CustomGroup { diff --git a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs index 6a3fecc0..9d506ff2 100644 --- a/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomLink/Demo.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; namespace SharedDemo.Demos.CustomLink { diff --git a/samples/SharedDemo/Demos/CustomNode.razor.cs b/samples/SharedDemo/Demos/CustomNode.razor.cs index 72747711..55a4e4fc 100644 --- a/samples/SharedDemo/Demos/CustomNode.razor.cs +++ b/samples/SharedDemo/Demos/CustomNode.razor.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; +using Blazor.Diagrams.Core.Geometry; namespace SharedDemo.Demos { diff --git a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs index 36d54d44..54adfc29 100644 --- a/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs +++ b/samples/SharedDemo/Demos/CustomPort/Demo.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; namespace SharedDemo.Demos.CustomPort { diff --git a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs index d52b6a22..1f6874f4 100644 --- a/samples/SharedDemo/Demos/DynamicInsertions.razor.cs +++ b/samples/SharedDemo/Demos/DynamicInsertions.razor.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; +using Blazor.Diagrams.Core.Geometry; using System; using System.Linq; diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index 339e4e06..6312a27a 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using System.Collections.Generic; diff --git a/samples/SharedDemo/Demos/Grouping.razor.cs b/samples/SharedDemo/Demos/Grouping.razor.cs index bc7caee3..5c858464 100644 --- a/samples/SharedDemo/Demos/Grouping.razor.cs +++ b/samples/SharedDemo/Demos/Grouping.razor.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; +using Blazor.Diagrams.Core.Geometry; namespace SharedDemo.Demos { diff --git a/samples/SharedDemo/Demos/Locked.razor.cs b/samples/SharedDemo/Demos/Locked.razor.cs index cbed95b2..a4e43536 100644 --- a/samples/SharedDemo/Demos/Locked.razor.cs +++ b/samples/SharedDemo/Demos/Locked.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; namespace SharedDemo diff --git a/samples/SharedDemo/Demos/Performance.razor.cs b/samples/SharedDemo/Demos/Performance.razor.cs index 6d935db1..3a04162d 100644 --- a/samples/SharedDemo/Demos/Performance.razor.cs +++ b/samples/SharedDemo/Demos/Performance.razor.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; +using Blazor.Diagrams.Core.Geometry; namespace SharedDemo.Demos { diff --git a/samples/SharedDemo/Demos/Simple.razor.cs b/samples/SharedDemo/Demos/Simple.razor.cs index 309305e0..96494b90 100644 --- a/samples/SharedDemo/Demos/Simple.razor.cs +++ b/samples/SharedDemo/Demos/Simple.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; namespace SharedDemo @@ -16,6 +16,12 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); + diagram.Links.Add(new LinkModel(node1, node2) + { + SourceMarker = LinkMarker.Arrow, + TargetMarker = LinkMarker.Arrow, + Segmentable = true + }); diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); } diff --git a/samples/SharedDemo/Demos/SnapToGrid.razor.cs b/samples/SharedDemo/Demos/SnapToGrid.razor.cs index c7ced383..8e4902e7 100644 --- a/samples/SharedDemo/Demos/SnapToGrid.razor.cs +++ b/samples/SharedDemo/Demos/SnapToGrid.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; namespace SharedDemo diff --git a/samples/SharedDemo/Demos/ZoomToFit.razor.cs b/samples/SharedDemo/Demos/ZoomToFit.razor.cs index 0bd1289b..70871063 100644 --- a/samples/SharedDemo/Demos/ZoomToFit.razor.cs +++ b/samples/SharedDemo/Demos/ZoomToFit.razor.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core; using Microsoft.AspNetCore.Components; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; namespace SharedDemo.Demos { diff --git a/src/Blazor.Diagrams.Algorithms/Extensions/PointExtensions.cs b/src/Blazor.Diagrams.Algorithms/Extensions/PointExtensions.cs index e46ff247..7471dbfa 100644 --- a/src/Blazor.Diagrams.Algorithms/Extensions/PointExtensions.cs +++ b/src/Blazor.Diagrams.Algorithms/Extensions/PointExtensions.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; using System; namespace Blazor.Diagrams.Algorithms.Extensions diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index f46fce51..667eaa13 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; using Microsoft.AspNetCore.Components.Web; using System; using System.Linq; diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index 4a8a6614..d3768e23 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -1,8 +1,7 @@ -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; -using System; using System.Linq; namespace Blazor.Diagrams.Core.Behaviors @@ -40,7 +39,7 @@ private void Diagram_MouseMove(Model model, MouseEventArgs e) var deltaX = (e.ClientX - _initialX) / Diagram.Zoom; var deltaY = (e.ClientY - _initialY) / Diagram.Zoom; - var sX = _ongoingLink.SourcePort.Position.X + _ongoingLink.SourcePort.Size.Width / 2; + var sX = _ongoingLink.SourcePort!.Position.X + _ongoingLink.SourcePort.Size.Width / 2; var sY = _ongoingLink.SourcePort.Position.Y + _ongoingLink.SourcePort.Size.Height / 2; _ongoingLink.OnGoingPosition = new Point(sX + deltaX, sY + deltaY); @@ -71,13 +70,14 @@ private void Diagram_MouseUp(Model model, MouseEventArgs e) return; } - if (!(model is PortModel port) || !_ongoingLink.SourcePort.CanAttachTo(port)) + if (!(model is PortModel port) || !_ongoingLink.SourcePort!.CanAttachTo(port)) { Diagram.Links.Remove(_ongoingLink); _ongoingLink = null; return; } + _ongoingLink.OnGoingPosition = null; _ongoingLink.SetTargetPort(port); _ongoingLink.Refresh(); port.Refresh(); @@ -91,7 +91,7 @@ private void Diagram_MouseUp(Model model, MouseEventArgs e) foreach (var port in Diagram.Nodes.SelectMany(n => n.Ports)) { if (_ongoingLink!.OnGoingPosition!.DistanceTo(port.Position) < Diagram.Options.Links.SnappingRadius && - _ongoingLink.SourcePort.CanAttachTo(port)) + _ongoingLink.SourcePort!.CanAttachTo(port)) return port; } diff --git a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs index b5448943..670d4784 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; using Microsoft.AspNetCore.Components.Web; using System; diff --git a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs index bf576282..87fe832a 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; using Microsoft.AspNetCore.Components.Web; using System; diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs index 9fbb9d56..e1c2afd7 100644 --- a/src/Blazor.Diagrams.Core/Delegates.cs +++ b/src/Blazor.Diagrams.Core/Delegates.cs @@ -1,12 +1,12 @@ -using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; namespace Blazor.Diagrams.Core { public delegate Point[] Router(Diagram diagram, BaseLinkModel link); - public delegate PathGeneratorResult PathGenerator(Diagram diagram, BaseLinkModel link, Point[] route); + public delegate PathGeneratorResult PathGenerator(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target); public delegate BaseLinkModel LinkFactory(Diagram diagram, PortModel sourcePort); diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index f4a47fcb..b5e5fdd2 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -1,9 +1,9 @@ using Blazor.Diagrams.Core.Behaviors; using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Layers; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using System; diff --git a/src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs index 98e6b830..4bd8351b 100644 --- a/src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs +++ b/src/Blazor.Diagrams.Core/Extensions/BaseLinkModelExtensions.cs @@ -5,6 +5,7 @@ namespace Blazor.Diagrams.Core.Extensions { + [Obsolete] public static class BaseLinkModelExtensions { private const double _margin = 125; diff --git a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs index ac1b01d9..d1e9e309 100644 --- a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs +++ b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; using System.Collections.Generic; using System.Linq; diff --git a/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs b/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs index 071bfa32..4c3a8faa 100644 --- a/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs +++ b/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs @@ -1,5 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; -using System; +using System; namespace Blazor.Diagrams.Core.Geometry { diff --git a/src/Blazor.Diagrams.Core/Geometry/IShape.cs b/src/Blazor.Diagrams.Core/Geometry/IShape.cs new file mode 100644 index 00000000..d9a8ad73 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Geometry/IShape.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Blazor.Diagrams.Core.Geometry +{ + public interface IShape + { + public IEnumerable GetIntersectionsWithLine(Line line); + } +} diff --git a/src/Blazor.Diagrams.Core/Geometry/Line.cs b/src/Blazor.Diagrams.Core/Geometry/Line.cs new file mode 100644 index 00000000..d5fca90e --- /dev/null +++ b/src/Blazor.Diagrams.Core/Geometry/Line.cs @@ -0,0 +1,43 @@ +namespace Blazor.Diagrams.Core.Geometry +{ + public class Line + { + public Line(Point start, Point end) + { + Start = start; + End = end; + } + + public Point Start { get; } + public Point End { get; } + + public Point? GetIntersection(Line line) + { + var pt1Dir = new Point(End.X - Start.X, End.Y - Start.Y); + var pt2Dir = new Point(line.End.X - line.Start.X, line.End.Y - line.Start.Y); + var det = (pt1Dir.X * pt2Dir.Y) - (pt1Dir.Y * pt2Dir.X); + var deltaPt = new Point(line.Start.X - Start.X, line.Start.Y - Start.Y); + var alpha = (deltaPt.X * pt2Dir.Y) - (deltaPt.Y * pt2Dir.X); + var beta = (deltaPt.X * pt1Dir.Y) - (deltaPt.Y * pt1Dir.X); + + if (det == 0 || alpha * det < 0 || beta * det < 0) + return null; + + if (det > 0) + { + if (alpha > det || beta > det) + return null; + + } + else + { + if (alpha < det || beta < det) + return null; + } + + return new Point(Start.X + (alpha * pt1Dir.X / det), Start.Y + (alpha * pt1Dir.Y / det)); + } + + public override string ToString() => $"Line from {Start} to {End}"; + } +} diff --git a/src/Blazor.Diagrams.Core/Models/Core/Point.cs b/src/Blazor.Diagrams.Core/Geometry/Point.cs similarity index 96% rename from src/Blazor.Diagrams.Core/Models/Core/Point.cs rename to src/Blazor.Diagrams.Core/Geometry/Point.cs index 50205b5c..ee6f0ff9 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Point.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Point.cs @@ -1,6 +1,6 @@ using System; -namespace Blazor.Diagrams.Core.Models.Core +namespace Blazor.Diagrams.Core.Geometry { public class Point { diff --git a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs b/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs similarity index 78% rename from src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs rename to src/Blazor.Diagrams.Core/Geometry/Rectangle.cs index 081e1a94..e0db2825 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Rectangle.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs @@ -1,8 +1,9 @@ using System; +using System.Collections.Generic; -namespace Blazor.Diagrams.Core.Models.Core +namespace Blazor.Diagrams.Core.Geometry { - public class Rectangle + public class Rectangle : IShape { public static Rectangle Zero { get; } = new Rectangle(0, 0, 0, 0); @@ -51,7 +52,7 @@ public bool Intersects(Rectangle r) var rectY = r.Top; var rectW = r.Width; var rectH = r.Height; - return (rectX < thisX + thisW) && (thisX < (rectX + rectW)) && (rectY < thisY + thisH) && (thisY < rectY + rectH); + return rectX < thisX + thisW && thisX < rectX + rectW && rectY < thisY + thisH && thisY < rectY + rectH; } public Rectangle Inflate(double horizontal, double vertical) @@ -71,6 +72,23 @@ public Rectangle Union(Rectangle r) public bool ContainsPoint(double x, double y) => x >= Left && x <= Right && y >= Top && y <= Bottom; + public IEnumerable GetIntersectionsWithLine(Line line) + { + var borders = new[] { + new Line(NorthWest, NorthEast), + new Line(NorthEast, SouthEast), + new Line(SouthWest, SouthEast), + new Line(NorthWest, SouthWest) + }; + + for (var i = 0; i < borders.Length; i++) + { + var intersectionPt = borders[i].GetIntersection(line); + if (intersectionPt != null) + yield return intersectionPt; + } + } + public Point Center => new Point(Left + Width / 2, Top + Height / 2); public Point NorthEast => new Point(Right, Top); public Point SouthEast => new Point(Right, Bottom); diff --git a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs new file mode 100644 index 00000000..7f90cae2 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs @@ -0,0 +1,11 @@ +using Blazor.Diagrams.Core.Models; + +namespace Blazor.Diagrams.Core.Geometry +{ + public delegate IShape ShapeDefiner(NodeModel node); + + public static class Shapes + { + public static IShape Rectangle(NodeModel node) => new Rectangle(node.Position, node.Size!); + } +} diff --git a/src/Blazor.Diagrams.Core/Models/Core/Size.cs b/src/Blazor.Diagrams.Core/Geometry/Size.cs similarity index 92% rename from src/Blazor.Diagrams.Core/Models/Core/Size.cs rename to src/Blazor.Diagrams.Core/Geometry/Size.cs index 32559404..487b8f19 100644 --- a/src/Blazor.Diagrams.Core/Models/Core/Size.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Size.cs @@ -1,4 +1,4 @@ -namespace Blazor.Diagrams.Core.Models.Core +namespace Blazor.Diagrams.Core.Geometry { public class Size { diff --git a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs index ded1fe73..641f57fd 100644 --- a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs +++ b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs @@ -8,26 +8,42 @@ public LinkLayer(Diagram diagram) : base(diagram) { } protected override void OnItemAdded(BaseLinkModel link) { - link.SourcePort.AddLink(link); - link.TargetPort?.AddLink(link); + if (!link.IsPortless) + { + link.SourcePort!.AddLink(link); + link.TargetPort?.AddLink(link); - link.SourcePort.Refresh(); - link.TargetPort?.Refresh(); + link.SourcePort.Refresh(); + link.TargetPort?.Refresh(); + } + else + { + link.SourceNode.AddLink(link); + link.TargetNode?.AddLink(link); + } - link.SourcePort.Parent.Group?.Refresh(); - link.TargetPort?.Parent.Group?.Refresh(); + link.SourceNode.Group?.Refresh(); + link.TargetNode?.Group?.Refresh(); } protected override void OnItemRemoved(BaseLinkModel link) { - link.SourcePort.RemoveLink(link); - link.TargetPort?.RemoveLink(link); + if (!link.IsPortless) + { + link.SourcePort!.RemoveLink(link); + link.TargetPort?.RemoveLink(link); - link.SourcePort.Refresh(); - link.TargetPort?.Refresh(); + link.SourcePort.Refresh(); + link.TargetPort?.Refresh(); + } + else + { + link.SourceNode.AddLink(link); + link.TargetNode?.AddLink(link); + } - link.SourcePort.Parent.Group?.Refresh(); - link.TargetPort?.Parent.Group?.Refresh(); + link.SourceNode.Group?.Refresh(); + link.TargetNode?.Group?.Refresh(); } } } diff --git a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs index d1becbdb..d9bdb755 100644 --- a/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/Base/BaseLinkModel.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; using System; using System.Collections.Generic; @@ -10,28 +10,47 @@ public abstract class BaseLinkModel : SelectableModel /// An event that fires when the SourcePort changes. /// The PortModel instance in the parameters is the old value. /// - public event Action? SourcePortChanged; + public event Action? SourcePortChanged; /// /// An event that fires when the TargetPort changes. /// The PortModel instance in the parameters is the old value. /// public event Action? TargetPortChanged; + public BaseLinkModel(NodeModel sourceNode, NodeModel? targetNode) + { + SourceNode = sourceNode; + TargetNode = targetNode; + } + + public BaseLinkModel(string id, NodeModel sourceNode, NodeModel? targetNode) : base(id) + { + SourceNode = sourceNode; + TargetNode = targetNode; + } + public BaseLinkModel(PortModel sourcePort, PortModel? targetPort = null) { SourcePort = sourcePort; TargetPort = targetPort; + SourceNode = SourcePort.Parent; + TargetNode = targetPort?.Parent; } public BaseLinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) : base(id) { SourcePort = sourcePort; TargetPort = targetPort; + SourceNode = SourcePort.Parent; + TargetNode = targetPort?.Parent; } - public PortModel SourcePort { get; private set; } + public NodeModel SourceNode { get; } + public NodeModel? TargetNode { get; } + public PortModel? SourcePort { get; private set; } public PortModel? TargetPort { get; private set; } - public bool IsAttached => TargetPort != null; + public bool IsAttached => TargetNode != null || TargetPort != null; + public bool IsPortless => SourcePort == null; public Point? OnGoingPosition { get; set; } public Router? Router { get; set; } public PathGenerator? PathGenerator { get; set; } @@ -47,7 +66,7 @@ public void SetSourcePort(PortModel port) return; var old = SourcePort; - SourcePort.RemoveLink(this); + SourcePort?.RemoveLink(this); SourcePort = port; SourcePort.AddLink(this); SourcePortChanged?.Invoke(old); diff --git a/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs b/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs index 0a2baf33..cb43afcd 100644 --- a/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs +++ b/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; namespace Blazor.Diagrams.Core.Models.Base { diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs index 068be035..14931d84 100644 --- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs +++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; using System.Collections.Generic; using System.Linq; @@ -61,6 +61,7 @@ public override void SetPosition(double x, double y) node.UpdatePositionSilently(deltaX, deltaY); Refresh(); + RefreshLinks(); } public override void UpdatePositionSilently(double deltaX, double deltaY) diff --git a/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs b/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs index 5d8d366d..7744e885 100644 --- a/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs +++ b/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; namespace Blazor.Diagrams.Core.Models { diff --git a/src/Blazor.Diagrams.Core/Models/LinkModel.cs b/src/Blazor.Diagrams.Core/Models/LinkModel.cs index daf414ae..d2b08373 100644 --- a/src/Blazor.Diagrams.Core/Models/LinkModel.cs +++ b/src/Blazor.Diagrams.Core/Models/LinkModel.cs @@ -6,8 +6,12 @@ public class LinkModel : BaseLinkModel { public LinkModel(PortModel sourcePort, PortModel? targetPort = null) : base(sourcePort, targetPort) { } + public LinkModel(NodeModel sourceNode, NodeModel? targetNode) : base(sourceNode, targetNode) { } + public LinkModel(string id, PortModel sourcePort, PortModel? targetPort = null) : base(id, sourcePort, targetPort) { } + public LinkModel(string id, NodeModel sourceNode, NodeModel? targetNode) : base(id, sourceNode, targetNode) { } + public string? Color { get; set; } public string? SelectedColor { get; set; } public double Width { get; set; } = 2; diff --git a/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs b/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs index efa12767..508acda9 100644 --- a/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs +++ b/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; namespace Blazor.Diagrams.Core.Models { diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index 78c3da1b..bc9c0621 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -1,8 +1,7 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; namespace Blazor.Diagrams.Core.Models @@ -10,22 +9,29 @@ namespace Blazor.Diagrams.Core.Models public class NodeModel : MovableModel { private readonly List _ports = new List(); + private readonly List _links = new List(); + private readonly ShapeDefiner _shapeDefiner; private Size? _size; public event Action? SizeChanged; public event Action? Moving; - public NodeModel(Point? position = null, RenderLayer layer = RenderLayer.HTML) : base(position) + public NodeModel(Point? position = null, RenderLayer layer = RenderLayer.HTML, + ShapeDefiner? shape = null) : base(position) { Layer = layer; + _shapeDefiner = shape ?? Shapes.Rectangle; } - public NodeModel(string id, Point? position = null, RenderLayer layer = RenderLayer.HTML) : base(id, position) + public NodeModel(string id, Point? position = null, RenderLayer layer = RenderLayer.HTML, + ShapeDefiner? shape = null) : base(id, position) { Layer = layer; + _shapeDefiner = shape ?? Shapes.Rectangle; } public RenderLayer Layer { get; } + public IShape Shape => _shapeDefiner(this); public Size? Size { get => _size; @@ -40,7 +46,8 @@ public Size? Size } public GroupModel? Group { get; internal set; } - public ReadOnlyCollection Ports => _ports.AsReadOnly(); + public IReadOnlyList Ports => _ports; + public IReadOnlyList Links => _links; public IEnumerable AllLinks => Ports.SelectMany(p => p.Links); public PortModel AddPort(PortModel port) @@ -62,6 +69,14 @@ public void RefreshAll() _ports.ForEach(p => p.RefreshAll()); } + public void RefreshLinks() + { + foreach (var link in Links) + { + link.Refresh(); + } + } + public void ReinitializePorts() { foreach (var port in Ports) @@ -81,6 +96,7 @@ public override void SetPosition(double x, double y) UpdatePortPositions(deltaX, deltaY); Refresh(); + RefreshLinks(); Moving?.Invoke(this); } @@ -123,5 +139,9 @@ private void UpdatePortPositions(double deltaX, double deltaY) port.RefreshLinks(); } } + + internal void AddLink(BaseLinkModel link) => _links.Add(link); + + internal void RemoveLink(BaseLinkModel link) => _links.Remove(link); } } \ No newline at end of file diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index aaff83aa..0dcf889a 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs index 018893be..780deb17 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; namespace Blazor.Diagrams.Core { diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs index b8450680..8f4ff88a 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Smooth.cs @@ -1,7 +1,6 @@ using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; using System; namespace Blazor.Diagrams.Core @@ -10,8 +9,10 @@ public static partial class PathGenerators { private const double _margin = 125; - public static PathGeneratorResult Smooth(Diagram _, BaseLinkModel link, Point[] route) + public static PathGeneratorResult Smooth(Diagram _, BaseLinkModel link, Point[] route, Point source, Point target) { + route = ConcatRouteAndSourceAndTarget(route, source, target); + if (route.Length > 2) return CurveThroughPoints(route, link); @@ -64,11 +65,27 @@ private static PathGeneratorResult CurveThroughPoints(Point[] route, BaseLinkMod private static Point[] GetRouteWithCurvePoints(BaseLinkModel link, Point[] route) { - var cX = (route[0].X + route[1].X) / 2; - var cY = (route[0].Y + route[1].Y) / 2; - var curvePointA = GetCurvePoint(route[0].X, route[0].Y, cX, cY, link.SourcePort.Alignment); - var curvePointB = GetCurvePoint(route[1].X, route[1].Y, cX, cY, link.TargetPort?.Alignment); - return new[] { route[0], curvePointA, curvePointB, route[1] }; + if (link.IsPortless) + { + if (Math.Abs(route[0].X - route[1].X) >= Math.Abs(route[0].Y - route[1].Y)) + { + var cX = (route[0].X + route[1].X) / 2; + return new[] { route[0], new Point(cX, route[0].Y), new Point(cX, route[1].Y), route[1] }; + } + else + { + var cY = (route[0].Y + route[1].Y) / 2; + return new[] { route[0], new Point(route[0].X, cY), new Point(route[1].X, cY), route[1] }; + } + } + else + { + var cX = (route[0].X + route[1].X) / 2; + var cY = (route[0].Y + route[1].Y) / 2; + var curvePointA = GetCurvePoint(route[0].X, route[0].Y, cX, cY, link.SourcePort?.Alignment); + var curvePointB = GetCurvePoint(route[1].X, route[1].Y, cX, cY, link.TargetPort?.Alignment); + return new[] { route[0], curvePointA, curvePointB, route[1] }; + } } private static Point GetCurvePoint(double pX, double pY, double cX, double cY, PortAlignment? alignment) diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs index ec8639f0..4dd93a86 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Straight.cs @@ -1,14 +1,14 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; using System; namespace Blazor.Diagrams.Core { public static partial class PathGenerators { - public static PathGeneratorResult Straight(Diagram _, BaseLinkModel link, Point[] route) + public static PathGeneratorResult Straight(Diagram _, BaseLinkModel link, Point[] route, Point source, Point target) { - route = (Point[])route.Clone(); + route = ConcatRouteAndSourceAndTarget(route, source, target); double? sourceAngle = null; double? targetAngle = null; diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs index e836257c..55933276 100644 --- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs +++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerators.Utils.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; using System; namespace Blazor.Diagrams.Core @@ -22,5 +22,14 @@ public static double TargetMarkerAdjustement(Point[] route, double markerWidth) route[^1] = new Point(route[^1].X - xChange, route[^1].Y - yChange); return angleInRadians * 180 / Math.PI; } + + public static Point[] ConcatRouteAndSourceAndTarget(Point[] route, Point source, Point target) + { + var result = new Point[route.Length + 2]; + result[0] = source; + route.CopyTo(result, 1); + result[^1] = target; + return result; + } } } diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs index f0f7c824..e27f8431 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Normal.cs @@ -1,6 +1,5 @@ -using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; -using System; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models.Base; using System.Linq; namespace Blazor.Diagrams.Core @@ -9,18 +8,7 @@ public static partial class Routers { public static Point[] Normal(Diagram _, BaseLinkModel link) { - var route = new Point[link.Vertices.Count + 2]; - route[0] = link.SourceMarker == null ? link.SourcePort.MiddlePosition : GetPortPositionBasedOnAlignment(link.SourcePort); - - if (link.Vertices.Count > 0) - { - Array.Copy(link.Vertices.Select(v => v.Position).ToArray(), 0, route, 1, link.Vertices.Count); - } - - route[^1] = link.TargetPort == null - ? link.OnGoingPosition! - : link.TargetMarker == null ? link.TargetPort.MiddlePosition : GetPortPositionBasedOnAlignment(link.TargetPort); - return route; + return link.Vertices.Select(v => v.Position).ToArray(); } } } diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs index 9317522b..a0d89102 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; using System; using System.Collections.Generic; using System.Data; @@ -15,7 +15,10 @@ public static partial class Routers { public static Point[] Orthogonal(Diagram _, BaseLinkModel link) { - if (link.TargetPort == null || link.SourcePort.Parent.Size == null || link.TargetPort.Parent.Size == null) + if (link.IsPortless) + throw new Exception("Orthogonal router doesn't work with portless links yet"); + + if (link.TargetPort == null || link.SourcePort!.Parent.Size == null || link.TargetPort.Parent.Size == null) return Normal(_, link); var shapeMargin = 10; @@ -29,8 +32,8 @@ public static Point[] Orthogonal(Diagram _, BaseLinkModel link) var sideBVertical = IsVerticalSide(sideB); var originA = GetPortPositionBasedOnAlignment(link.SourcePort); var originB = GetPortPositionBasedOnAlignment(link.TargetPort); - var shapeA = link.SourcePort.Parent.GetBounds(includePorts: true); - var shapeB = link.TargetPort.Parent.GetBounds(includePorts: true); + var shapeA = link.SourcePort.Parent.GetBounds(includePorts: true)!; + var shapeB = link.TargetPort.Parent.GetBounds(includePorts: true)!; var inflatedA = shapeA.Inflate(shapeMargin, shapeMargin); var inflatedB = shapeB.Inflate(shapeMargin, shapeMargin); diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs index 6e344bc8..61fc7b5d 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Utils.cs @@ -1,5 +1,5 @@ -using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; namespace Blazor.Diagrams.Core { diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs index bdde30e7..939af8c1 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs @@ -1,5 +1,5 @@ using Blazor.Diagrams.Core; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Extensions; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; diff --git a/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs b/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs index 63c20053..a258488c 100644 --- a/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs +++ b/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using System; diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index e702a155..d07893f9 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -1,9 +1,11 @@ @using SvgPathProperties; + @{ var router = Link.Router ?? Diagram.Options.Links.DefaultRouter; var pathGenerator = Link.PathGenerator ?? Diagram.Options.Links.DefaultPathGenerator; var route = router(Diagram, Link); - var result = pathGenerator(Diagram, Link, route); + (var source, var target) = FindConnectionPoints(route); + var result = pathGenerator(Diagram, Link, route, source, target); var color = Link.Selected ? (Link.SelectedColor ?? Diagram.Options.Links.DefaultSelectedColor) : (Link.Color ?? Diagram.Options.Links.DefaultColor); var paths = Link.Labels.Count > 0 ? result.Paths.Select(p => new SVGPathProperties(p)).ToArray() : Array.Empty(); diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index 7594f149..7501fa6c 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -1,8 +1,9 @@ using Blazor.Diagrams.Core.Models; using Blazor.Diagrams.Core; using Microsoft.AspNetCore.Components; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components.Web; +using Blazor.Diagrams.Core.Geometry; +using System.Collections.Generic; namespace Blazor.Diagrams.Components { @@ -24,5 +25,51 @@ private void OnMouseDown(MouseEventArgs e, int index) Link.Vertices.Insert(index, vertex); Diagram.OnMouseDown(vertex, e); } + + private (Point source, Point target) FindConnectionPoints(Point[] route) + { + if (Link.SourcePort == null) // Portless + { + var source = Point.Zero; + var target = Point.Zero; + + if (Link.SourceNode.Size == null || Link.TargetNode?.Size == null) + return (source, target); + + var sourceCenter = Link.SourceNode.GetBounds().Center; + var targetCenter = Link.TargetNode?.GetBounds().Center ?? Link.OnGoingPosition; + var firstPt = route.Length > 0 ? route[0] : targetCenter; + var secondPt = route.Length > 0 ? route[0] : sourceCenter; + var sourceLine = new Line(firstPt, sourceCenter); + var targetLine = new Line(secondPt, targetCenter); + var sourceIntersections = Link.SourceNode.Shape.GetIntersectionsWithLine(sourceLine); + var targetIntersections = Link.TargetNode.Shape.GetIntersectionsWithLine(targetLine); + var sourceIntersection = GetClosestPointTo(sourceIntersections, firstPt); + var targetIntersection = GetClosestPointTo(targetIntersections, secondPt); + return (sourceIntersection ?? sourceCenter, targetIntersection ?? targetCenter); + } + else + { + return (Link.SourcePort.MiddlePosition, Link.TargetPort?.MiddlePosition ?? Link.OnGoingPosition); + } + } + + private Point GetClosestPointTo(IEnumerable points, Point point) + { + var minDist = double.MaxValue; + Point minPoint = null; + + foreach (var pt in points) + { + var dist = pt.DistanceTo(point); + if (dist < minDist) + { + minDist = dist; + minPoint = pt; + } + } + + return minPoint; + } } } diff --git a/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs index 69ac1f94..e204046a 100644 --- a/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/NavigatorWidget.razor.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using System; using System.Linq; diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs index feec1775..f2dfb966 100644 --- a/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/LinkLabelRenderer.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; using SvgPathProperties; diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index a37e2b5a..1aa6217e 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -1,7 +1,7 @@ using Blazor.Diagrams.Core; using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models; -using Blazor.Diagrams.Core.Models.Core; using Blazor.Diagrams.Extensions; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; @@ -55,6 +55,7 @@ public void OnResize(Size size) Node.Size = size; Node.Refresh(); + Node.RefreshLinks(); Node.ReinitializePorts(); } diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs index c126917e..d3350acc 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs @@ -6,7 +6,7 @@ using System; using System.Threading.Tasks; using Blazor.Diagrams.Extensions; -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; namespace Blazor.Diagrams.Components.Renderers { diff --git a/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs index 8e1cb03d..787de7cf 100644 --- a/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/SelectionBoxWidget.razor.cs @@ -1,6 +1,6 @@ using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models.Base; -using Blazor.Diagrams.Core.Models.Core; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using System; diff --git a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs index 0e0e9062..137c84d7 100644 --- a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs +++ b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs @@ -1,4 +1,4 @@ -using Blazor.Diagrams.Core.Models.Core; +using Blazor.Diagrams.Core.Geometry; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using System.Threading.Tasks; diff --git a/src/Blazor.Diagrams/_Imports.razor b/src/Blazor.Diagrams/_Imports.razor index 8ec66edd..71498dca 100644 --- a/src/Blazor.Diagrams/_Imports.razor +++ b/src/Blazor.Diagrams/_Imports.razor @@ -3,4 +3,4 @@ @using Blazor.Diagrams.Core.Extensions; @using Blazor.Diagrams.Core.Models; @using Blazor.Diagrams.Components.Renderers -@using Blazor.Diagrams.Core.Models.Core; +@using Blazor.Diagrams.Core.Geometry; From c8d261d51d517f45f00f458264f91e08c5eb3241 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sun, 14 Mar 2021 16:38:23 +0100 Subject: [PATCH 43/67] feat: Ellipse shape --- samples/SharedDemo/Demos/Simple.razor.cs | 6 -- src/Blazor.Diagrams.Core/Geometry/Ellipse.cs | 60 ++++++++++++++++++++ src/Blazor.Diagrams.Core/Geometry/Point.cs | 4 ++ src/Blazor.Diagrams.Core/Geometry/Shapes.cs | 8 +++ 4 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/Blazor.Diagrams.Core/Geometry/Ellipse.cs diff --git a/samples/SharedDemo/Demos/Simple.razor.cs b/samples/SharedDemo/Demos/Simple.razor.cs index 96494b90..6f9f5966 100644 --- a/samples/SharedDemo/Demos/Simple.razor.cs +++ b/samples/SharedDemo/Demos/Simple.razor.cs @@ -16,12 +16,6 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagram.Links.Add(new LinkModel(node1, node2) - { - SourceMarker = LinkMarker.Arrow, - TargetMarker = LinkMarker.Arrow, - Segmentable = true - }); diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); } diff --git a/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs b/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs new file mode 100644 index 00000000..9a41a6d4 --- /dev/null +++ b/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace Blazor.Diagrams.Core.Geometry +{ + public class Ellipse : IShape + { + public Ellipse(double x, double y, double rx, double ry) + { + X = x; + Y = y; + Rx = rx; + Ry = ry; + } + + public double X { get; } + public double Y { get; } + public double Rx { get; } + public double Ry { get; } + + public IEnumerable GetIntersectionsWithLine(Line line) + { + var a1 = line.Start; + var a2 = line.End; + var dir = new Point(line.End.X - line.Start.X, line.End.Y - line.Start.Y); + var diff = a1.Substract(X, Y); + var mDir = new Point(dir.X / (Rx * Rx), dir.Y / (Ry * Ry)); + var mDiff = new Point(diff.X / (Rx * Rx), diff.Y / (Ry * Ry)); + + var a = dir.Dot(mDir); + var b = dir.Dot(mDiff); + var c = diff.Dot(mDiff) - 1.0; + var d = b * b - a * c; + + if (d > 0) + { + var root = Math.Sqrt(d); + var ta = (-b - root) / a; + var tb = (-b + root) / a; + + if (ta >= 0 && 1 >= ta || tb >= 0 && 1 >= tb) + { + if (0 <= ta && ta <= 1) + yield return a1.Lerp(a2, ta); + + if (0 <= tb && tb <= 1) + yield return a1.Lerp(a2, tb); + } + } + else + { + var t = -b / a; + if (0 <= t && t <= 1) + { + yield return a1.Lerp(a2, t); + } + } + } + } +} diff --git a/src/Blazor.Diagrams.Core/Geometry/Point.cs b/src/Blazor.Diagrams.Core/Geometry/Point.cs index ee6f0ff9..28edd113 100644 --- a/src/Blazor.Diagrams.Core/Geometry/Point.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Point.cs @@ -15,6 +15,10 @@ public Point(double x, double y) public double X { get; } public double Y { get; } + public double Dot(Point other) => X * other.X + Y * other.Y; + public Point Lerp(Point other, double t) + => new Point(X * (1.0 - t) + other.X * t, Y * (1.0 - t) + other.Y * t); + // Maybe just make Points mutable? public Point Add(double value) => new Point(X + value, Y + value); public Point Add(double x, double y) => new Point(X + x, Y + y); diff --git a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs index 7f90cae2..08895b30 100644 --- a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs @@ -7,5 +7,13 @@ namespace Blazor.Diagrams.Core.Geometry public static class Shapes { public static IShape Rectangle(NodeModel node) => new Rectangle(node.Position, node.Size!); + + public static IShape Circle(NodeModel node) + { + var halfWidth = node.Size!.Width / 2; + var centerX = node.Position.X + halfWidth; + var centerY = node.Position.Y + node.Size!.Height / 2; + return new Ellipse(centerX, centerY, halfWidth, halfWidth); + } } } From cf6a21e77f85c123ca4d9a206f11da221cf30e7e Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sun, 14 Mar 2021 22:01:41 +0100 Subject: [PATCH 44/67] feat: Add Ellipse shape --- src/Blazor.Diagrams.Core/Geometry/Ellipse.cs | 12 ++++++------ src/Blazor.Diagrams.Core/Geometry/Shapes.cs | 11 ++++++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs b/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs index 9a41a6d4..022b80af 100644 --- a/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs @@ -5,16 +5,16 @@ namespace Blazor.Diagrams.Core.Geometry { public class Ellipse : IShape { - public Ellipse(double x, double y, double rx, double ry) + public Ellipse(double cx, double cy, double rx, double ry) { - X = x; - Y = y; + Cx = cx; + Cy = cy; Rx = rx; Ry = ry; } - public double X { get; } - public double Y { get; } + public double Cx { get; } + public double Cy { get; } public double Rx { get; } public double Ry { get; } @@ -23,7 +23,7 @@ public IEnumerable GetIntersectionsWithLine(Line line) var a1 = line.Start; var a2 = line.End; var dir = new Point(line.End.X - line.Start.X, line.End.Y - line.Start.Y); - var diff = a1.Substract(X, Y); + var diff = a1.Substract(Cx, Cy); var mDir = new Point(dir.X / (Rx * Rx), dir.Y / (Ry * Ry)); var mDiff = new Point(diff.X / (Rx * Rx), diff.Y / (Ry * Ry)); diff --git a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs index 08895b30..07d79978 100644 --- a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs @@ -12,8 +12,17 @@ public static IShape Circle(NodeModel node) { var halfWidth = node.Size!.Width / 2; var centerX = node.Position.X + halfWidth; - var centerY = node.Position.Y + node.Size!.Height / 2; + var centerY = node.Position.Y + node.Size.Height / 2; return new Ellipse(centerX, centerY, halfWidth, halfWidth); } + + public static IShape Ellipse(NodeModel node) + { + var halfWidth = node.Size!.Width / 2; + var halfHeight = node.Size.Height / 2; + var centerX = node.Position.X + halfWidth; + var centerY = node.Position.Y + halfHeight; + return new Ellipse(centerX, centerY, halfWidth, halfHeight); + } } } From b31e84a2c9411871d482bc07d92128079ed0c661 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 15 Mar 2021 18:03:59 +0100 Subject: [PATCH 45/67] fix: Refresh links on groups --- src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs b/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs index a258488c..e02e02c4 100644 --- a/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs +++ b/src/Blazor.Diagrams/Components/Groups/GroupContainer.razor.cs @@ -56,6 +56,7 @@ protected override void OnAfterRender(bool firstRender) if (_lastSize == null || !_lastSize.Equals(Group.Size)) { Group.ReinitializePorts(); + Group.RefreshLinks(); _lastSize = Group.Size; } } From e63135546ed081842b59f5c11f5643f5f87316d9 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 15 Mar 2021 19:16:10 +0100 Subject: [PATCH 46/67] refactor: Change PortModel.Initialized's setter to be public --- src/Blazor.Diagrams.Core/Models/PortModel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index 0dcf889a..14bac52f 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -33,7 +33,10 @@ public PortModel(string id, NodeModel parent, PortAlignment alignment = PortAlig public Point MiddlePosition => new Point(Position.X + Size.Width / 2, Position.Y + Size.Height / 2); public Size Size { get; set; } public ReadOnlyCollection Links => _links.AsReadOnly(); - public bool Initialized { get; internal set; } + /// + /// If set to false, a call to Refresh() will force the port to update its position/size using JS + /// + public bool Initialized { get; set; } public void RefreshAll() { From 6ea1252211c8cde26c5f86e9e54cee8a5cdbe7b5 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 15 Mar 2021 20:00:33 +0100 Subject: [PATCH 47/67] refactor: change Shape to ShapeDefiner so that it's clear how it's defined --- src/Blazor.Diagrams.Core/Models/NodeModel.cs | 9 +++++---- src/Blazor.Diagrams/Components/LinkWidget.razor.cs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs index bc9c0621..ed305009 100644 --- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs +++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs @@ -10,7 +10,6 @@ public class NodeModel : MovableModel { private readonly List _ports = new List(); private readonly List _links = new List(); - private readonly ShapeDefiner _shapeDefiner; private Size? _size; public event Action? SizeChanged; @@ -20,18 +19,18 @@ public NodeModel(Point? position = null, RenderLayer layer = RenderLayer.HTML, ShapeDefiner? shape = null) : base(position) { Layer = layer; - _shapeDefiner = shape ?? Shapes.Rectangle; + ShapeDefiner = shape ?? Shapes.Rectangle; } public NodeModel(string id, Point? position = null, RenderLayer layer = RenderLayer.HTML, ShapeDefiner? shape = null) : base(id, position) { Layer = layer; - _shapeDefiner = shape ?? Shapes.Rectangle; + ShapeDefiner = shape ?? Shapes.Rectangle; } public RenderLayer Layer { get; } - public IShape Shape => _shapeDefiner(this); + public ShapeDefiner ShapeDefiner { get; } public Size? Size { get => _size; @@ -130,6 +129,8 @@ public virtual void UpdatePositionSilently(double deltaX, double deltaY) return new Rectangle(left, top, right, bottom); } + public IShape GetShape() => ShapeDefiner(this); + private void UpdatePortPositions(double deltaX, double deltaY) { // Save some JS calls and update ports directly here diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index 7501fa6c..aff38a5b 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -42,8 +42,8 @@ private void OnMouseDown(MouseEventArgs e, int index) var secondPt = route.Length > 0 ? route[0] : sourceCenter; var sourceLine = new Line(firstPt, sourceCenter); var targetLine = new Line(secondPt, targetCenter); - var sourceIntersections = Link.SourceNode.Shape.GetIntersectionsWithLine(sourceLine); - var targetIntersections = Link.TargetNode.Shape.GetIntersectionsWithLine(targetLine); + var sourceIntersections = Link.SourceNode.GetShape().GetIntersectionsWithLine(sourceLine); + var targetIntersections = Link.TargetNode.GetShape().GetIntersectionsWithLine(targetLine); var sourceIntersection = GetClosestPointTo(sourceIntersections, firstPt); var targetIntersection = GetClosestPointTo(targetIntersections, secondPt); return (sourceIntersection ?? sourceCenter, targetIntersection ?? targetCenter); From 855568e4754bce8f21f129c7e41da3baf2e69ae8 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 15 Mar 2021 20:04:57 +0100 Subject: [PATCH 48/67] feat: Don't show links unless they are initialized --- src/Blazor.Diagrams/Components/LinkWidget.razor | 3 +++ src/Blazor.Diagrams/Components/LinkWidget.razor.cs | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index d07893f9..791102b7 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -5,6 +5,9 @@ var pathGenerator = Link.PathGenerator ?? Diagram.Options.Links.DefaultPathGenerator; var route = router(Diagram, Link); (var source, var target) = FindConnectionPoints(route); + if (source == null || target == null) + return; + var result = pathGenerator(Diagram, Link, route, source, target); var color = Link.Selected ? (Link.SelectedColor ?? Diagram.Options.Links.DefaultSelectedColor) : (Link.Color ?? Diagram.Options.Links.DefaultColor); diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index aff38a5b..f3a6f307 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components.Web; using Blazor.Diagrams.Core.Geometry; using System.Collections.Generic; +using System; namespace Blazor.Diagrams.Components { @@ -30,11 +31,8 @@ private void OnMouseDown(MouseEventArgs e, int index) { if (Link.SourcePort == null) // Portless { - var source = Point.Zero; - var target = Point.Zero; - if (Link.SourceNode.Size == null || Link.TargetNode?.Size == null) - return (source, target); + return (null, null); var sourceCenter = Link.SourceNode.GetBounds().Center; var targetCenter = Link.TargetNode?.GetBounds().Center ?? Link.OnGoingPosition; @@ -50,6 +48,9 @@ private void OnMouseDown(MouseEventArgs e, int index) } else { + if (!Link.SourcePort.Initialized || Link.TargetPort?.Initialized == false) + return (null, null); + return (Link.SourcePort.MiddlePosition, Link.TargetPort?.MiddlePosition ?? Link.OnGoingPosition); } } From ceeda8103e052247b4868cb896cb9f7f12595441 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Fri, 19 Mar 2021 21:47:29 +0100 Subject: [PATCH 49/67] fix: PortRender not taking into account it's parent's RenderLayer --- .../Components/LinkWidget.razor.cs | 1 - .../{PortRenderer.razor.cs => PortRenderer.cs} | 17 ++++++++++++++++- .../Components/Renderers/PortRenderer.razor | 9 --------- 3 files changed, 16 insertions(+), 11 deletions(-) rename src/Blazor.Diagrams/Components/Renderers/{PortRenderer.razor.cs => PortRenderer.cs} (70%) delete mode 100644 src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index f3a6f307..3f8a3e55 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Components.Web; using Blazor.Diagrams.Core.Geometry; using System.Collections.Generic; -using System; namespace Blazor.Diagrams.Components { diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs similarity index 70% rename from src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs rename to src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs index d3350acc..17613bef 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor.cs +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs @@ -7,10 +7,11 @@ using System.Threading.Tasks; using Blazor.Diagrams.Extensions; using Blazor.Diagrams.Core.Geometry; +using Microsoft.AspNetCore.Components.Rendering; namespace Blazor.Diagrams.Components.Renderers { - public partial class PortRenderer : IDisposable + public class PortRenderer : ComponentBase, IDisposable { private bool _shouldRender = true; private ElementReference _element; @@ -44,6 +45,20 @@ protected override void OnInitialized() protected override bool ShouldRender() => _shouldRender; + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, Port.Parent.Layer == RenderLayer.HTML ? "div" : "g"); + builder.AddAttribute(1, "class", "port" + " " + (Port.Alignment.ToString().ToLower()) + " " + (Port.Links.Count > 0 ? "has-links" : "") + " " + (Class)); + builder.AddAttribute(2, "data-port-id", Port.Id); + builder.AddAttribute(3, "onmousedown", EventCallback.Factory.Create(this, OnMouseDown)); + builder.AddEventStopPropagationAttribute(4, "onmousedown", true); + builder.AddAttribute(5, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); + builder.AddEventStopPropagationAttribute(6, "onmouseup", true); + builder.AddElementReferenceCapture(7, (__value) => { _element = __value; }); + builder.AddContent(8, ChildContent); + builder.CloseElement(); + } + protected override async Task OnAfterRenderAsync(bool firstRender) { await base.OnAfterRenderAsync(firstRender); diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor deleted file mode 100644 index 9d946ecc..00000000 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.razor +++ /dev/null @@ -1,9 +0,0 @@ -
- @ChildContent -
\ No newline at end of file From 472f6189f93687f55fc39d8049bb0985ba7a3e82 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 20 Mar 2021 11:30:49 +0100 Subject: [PATCH 50/67] feat: Add Touch events + Touch support on link dragging --- .../Behaviors/DragNewLinkBehavior.cs | 54 +++++++++++++----- src/Blazor.Diagrams.Core/Diagram.cs | 9 +++ src/Blazor.Diagrams.Core/Models/PortModel.cs | 2 + .../Components/DiagramCanvas.razor | 3 +- .../Components/DiagramCanvas.razor.cs | 12 ++-- .../Components/Renderers/PortRenderer.cs | 37 ++++++++++-- src/Blazor.Diagrams/wwwroot/style.css | 1 + src/Blazor.Diagrams/wwwroot/style.min.css | 2 +- src/Blazor.Diagrams/wwwroot/style.min.css.gz | Bin 392 -> 402 bytes 9 files changed, 95 insertions(+), 25 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs index d3768e23..33e8fdc6 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs @@ -14,31 +14,54 @@ public class DragNewLinkBehavior : Behavior public DragNewLinkBehavior(Diagram diagram) : base(diagram) { - Diagram.MouseDown += Diagram_MouseDown; - Diagram.MouseMove += Diagram_MouseMove; - Diagram.MouseUp += Diagram_MouseUp; + Diagram.MouseDown += OnMouseDown; + Diagram.MouseMove += OnMouseMove; + Diagram.MouseUp += OnMouseUp; + Diagram.TouchStart += OnTouchStart; + Diagram.TouchMove += OnTouchMove; + Diagram.TouchEnd += OnTouchEnd; } - private void Diagram_MouseDown(Model model, MouseEventArgs e) + private void OnTouchStart(Model model, TouchEventArgs e) + => Start(model, e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY); + + private void OnTouchMove(Model model, TouchEventArgs e) + => Move(model, e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY); + + private void OnTouchEnd(Model model, TouchEventArgs e) => End(model); + + private void OnMouseDown(Model model, MouseEventArgs e) + { + if (e.Button != (int)MouseEventButton.Left) + return; + + Start(model, e.ClientX, e.ClientY); + } + + private void OnMouseMove(Model model, MouseEventArgs e) => Move(model, e.ClientX, e.ClientY); + + private void OnMouseUp(Model model, MouseEventArgs e) => End(model); + + private void Start(Model model, double clientX, double clientY) { - if (!(model is PortModel port) || port.Locked || e.Button != (int)MouseEventButton.Left) + if (!(model is PortModel port) || port.Locked) return; - _initialX = e.ClientX; - _initialY = e.ClientY; + _initialX = clientX; + _initialY = clientY; _ongoingLink = Diagram.Options.Links.Factory(Diagram, port); _ongoingLink.OnGoingPosition = new Point(port.Position.X + port.Size.Width / 2, port.Position.Y + port.Size.Height / 2); Diagram.Links.Add(_ongoingLink); } - private void Diagram_MouseMove(Model model, MouseEventArgs e) + private void Move(Model model, double clientX, double clientY) { if (_ongoingLink == null || model != null) return; - var deltaX = (e.ClientX - _initialX) / Diagram.Zoom; - var deltaY = (e.ClientY - _initialY) / Diagram.Zoom; + var deltaX = (clientX - _initialX) / Diagram.Zoom; + var deltaY = (clientY - _initialY) / Diagram.Zoom; var sX = _ongoingLink.SourcePort!.Position.X + _ongoingLink.SourcePort.Size.Width / 2; var sY = _ongoingLink.SourcePort.Position.Y + _ongoingLink.SourcePort.Size.Height / 2; @@ -59,7 +82,7 @@ private void Diagram_MouseMove(Model model, MouseEventArgs e) _ongoingLink.Refresh(); } - private void Diagram_MouseUp(Model model, MouseEventArgs e) + private void End(Model model) { if (_ongoingLink == null) return; @@ -100,9 +123,12 @@ private void Diagram_MouseUp(Model model, MouseEventArgs e) public override void Dispose() { - Diagram.MouseDown -= Diagram_MouseDown; - Diagram.MouseMove -= Diagram_MouseMove; - Diagram.MouseUp -= Diagram_MouseUp; + Diagram.MouseDown -= OnMouseDown; + Diagram.MouseMove -= OnMouseMove; + Diagram.MouseUp -= OnMouseUp; + Diagram.TouchStart -= OnTouchStart; + Diagram.TouchMove -= OnTouchMove; + Diagram.TouchEnd -= OnTouchEnd; } } } diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index b5e5fdd2..4aacd688 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -26,6 +26,9 @@ public class Diagram : Model public event Action? KeyDown; public event Action? Wheel; public event Action? MouseClick; + public event Action? TouchStart; + public event Action? TouchMove; + public event Action? TouchEnd; public event Action? SelectionChanged; public event Action? GroupAdded; @@ -335,5 +338,11 @@ public Point GetRelativePoint(double clientX, double clientY) internal void OnWheel(WheelEventArgs e) => Wheel?.Invoke(e); internal void OnMouseClick(Model model, MouseEventArgs e) => MouseClick?.Invoke(model, e); + + internal void OnTouchStart(Model model, TouchEventArgs e) => TouchStart?.Invoke(model, e); + + internal void OnTouchMove(Model model, TouchEventArgs e) => TouchMove?.Invoke(model, e); + + internal void OnTouchEnd(Model model, TouchEventArgs e) => TouchEnd?.Invoke(model, e); } } diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs index 14bac52f..d6ca5553 100644 --- a/src/Blazor.Diagrams.Core/Models/PortModel.cs +++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs @@ -51,6 +51,8 @@ public void RefreshAll() public virtual bool CanAttachTo(PortModel port) => port != this && !port.Locked && Parent != port.Parent; + public Rectangle GetBounds() => new Rectangle(Position, Size); + internal void AddLink(BaseLinkModel link) => _links.Add(link); internal void RemoveLink(BaseLinkModel link) => _links.Remove(link); diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index 1bd3a6b5..878ed486 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -7,7 +7,8 @@ @onmouseup="OnMouseUp" @onkeydown="OnKeyDown" @onwheel="OnWheel" - @onwheel:stopPropagation> + @onwheel:stopPropagation + @ontouchmove="OnTouchMove"> @* Links *@ diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs index 939af8c1..2e00d75b 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs @@ -63,15 +63,17 @@ protected override bool ShouldRender() return false; } - protected void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(null, e); + private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(null, e); - protected void OnMouseMove(MouseEventArgs e) => Diagram.OnMouseMove(null, e); + private void OnMouseMove(MouseEventArgs e) => Diagram.OnMouseMove(null, e); - protected void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(null, e); + private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(null, e); - protected void OnKeyDown(KeyboardEventArgs e) => Diagram.OnKeyDown(e); + private void OnKeyDown(KeyboardEventArgs e) => Diagram.OnKeyDown(e); - protected void OnWheel(WheelEventArgs e) => Diagram.OnWheel(e); + private void OnWheel(WheelEventArgs e) => Diagram.OnWheel(e); + + private void OnTouchMove(TouchEventArgs e) => Diagram.OnTouchMove(null, e); private void OnDiagramChanged() { diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs index 17613bef..76cf4e05 100644 --- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs @@ -8,6 +8,7 @@ using Blazor.Diagrams.Extensions; using Blazor.Diagrams.Core.Geometry; using Microsoft.AspNetCore.Components.Rendering; +using System.Linq; namespace Blazor.Diagrams.Components.Renderers { @@ -54,8 +55,13 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.AddEventStopPropagationAttribute(4, "onmousedown", true); builder.AddAttribute(5, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); builder.AddEventStopPropagationAttribute(6, "onmouseup", true); - builder.AddElementReferenceCapture(7, (__value) => { _element = __value; }); - builder.AddContent(8, ChildContent); + builder.AddAttribute(7, "ontouchstart", EventCallback.Factory.Create(this, OnTouchStart)); + builder.AddEventStopPropagationAttribute(8, "ontouchstart", true); + builder.AddAttribute(9, "ontouchend", EventCallback.Factory.Create(this, OnTouchEnd)); + builder.AddEventStopPropagationAttribute(10, "ontouchend", true); + builder.AddEventPreventDefaultAttribute(11, "ontouchend", true); + builder.AddElementReferenceCapture(12, (__value) => { _element = __value; }); + builder.AddContent(13, ChildContent); builder.CloseElement(); } @@ -70,9 +76,32 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } } - protected virtual void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Port, e); + private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Port, e); - protected virtual void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Port, e); + private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Port, e); + + private void OnTouchStart(TouchEventArgs e) => Diagram.OnTouchStart(Port, e); + + private void OnTouchEnd(TouchEventArgs e) + => Diagram.OnTouchEnd(FindPortOn(e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY), e); + + private PortModel FindPortOn(double clientX, double clientY) + { + var allPorts = Diagram.Nodes.SelectMany(n => n.Ports) + .Union(Diagram.Groups.SelectMany(g => g.Ports)); + + foreach (var port in allPorts) + { + if (!port.Initialized) + continue; + + var relativePt = Diagram.GetRelativeMousePoint(clientX, clientY); + if (port.GetBounds().ContainsPoint(relativePt)) + return port; + } + + return null; + } private async Task UpdateDimensions() { diff --git a/src/Blazor.Diagrams/wwwroot/style.css b/src/Blazor.Diagrams/wwwroot/style.css index 737d479b..99b47920 100644 --- a/src/Blazor.Diagrams/wwwroot/style.css +++ b/src/Blazor.Diagrams/wwwroot/style.css @@ -6,6 +6,7 @@ overflow: hidden; cursor: -webkit-grab; cursor: grab; + touch-action: none; } .diagram-svg-layer, .diagram-html-layer { diff --git a/src/Blazor.Diagrams/wwwroot/style.min.css b/src/Blazor.Diagrams/wwwroot/style.min.css index 9d5df7cc..8de9f327 100644 --- a/src/Blazor.Diagrams/wwwroot/style.min.css +++ b/src/Blazor.Diagrams/wwwroot/style.min.css @@ -1 +1 @@ -.diagram-canvas{width:100%;height:100%;position:relative;outline:none;overflow:hidden;cursor:-webkit-grab;cursor:grab;}.diagram-svg-layer,.diagram-html-layer{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;width:100%;height:100%;overflow:visible;}.html-layer,.svg-layer{position:absolute;pointer-events:none;-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;width:100%;height:100%;overflow:visible;}.node{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:move;pointer-events:all;}.node.locked{cursor:pointer;}.link{pointer-events:visiblePainted;cursor:pointer;}.link path.selection-helper:hover{stroke-opacity:.05;}.diagram-navigator{z-index:10;}.diagram-navigator .current-view{position:absolute;border:2px solid #000;}.group{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:move;pointer-events:all;}.group .layer{position:absolute;overflow:visible;pointer-events:none;}.link foreignObject{overflow:visible;pointer-events:none;} \ No newline at end of file +.diagram-canvas{width:100%;height:100%;position:relative;outline:none;overflow:hidden;cursor:-webkit-grab;cursor:grab;touch-action:none;}.diagram-svg-layer,.diagram-html-layer{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;width:100%;height:100%;overflow:visible;}.html-layer,.svg-layer{position:absolute;pointer-events:none;-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;width:100%;height:100%;overflow:visible;}.node{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:move;pointer-events:all;}.node.locked{cursor:pointer;}.link{pointer-events:visiblePainted;cursor:pointer;}.link path.selection-helper:hover{stroke-opacity:.05;}.diagram-navigator{z-index:10;}.diagram-navigator .current-view{position:absolute;border:2px solid #000;}.group{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:move;pointer-events:all;}.group .layer{position:absolute;overflow:visible;pointer-events:none;}.link foreignObject{overflow:visible;pointer-events:none;} \ No newline at end of file diff --git a/src/Blazor.Diagrams/wwwroot/style.min.css.gz b/src/Blazor.Diagrams/wwwroot/style.min.css.gz index 9b86c3f97b05fd959462136e78f06595af0193c7..f3fb7e1cdd4bd64e6a0323aa843d97f4a42563a0 100644 GIT binary patch literal 402 zcmV;D0d4*tiwFP!000003eA#1PV6uYhObfyy&b6n5}Wi8yep)Qr!kYpRpNFADo)0N zBXI@XnNHsmQzVwGn*L7g&%geUxA)SaZIkU(W8-M^bU?@6+-d!%@_?=9^SP&tj1*0R zU>QfKXyAYmOr$73BcxrRgYoDb#HtxmrexFs>OHcO0rf>YotcKlE8Cn}o;tj)AbD(6 zu+NZgSIKi1&f3J(8(jt1v3$}AP**iEQzwTN)7F{7z#s}nhNNJG$oV|$g2l{)IQ?SaNDemmE-XV9$P57*ULZSq*;4bh zcqJ^^=mg`d0XBpMrVP~V!A;BM<(?EJhx_Tvvt^IJc2-@r`KIVC`|`{oJ1P&Mhh+S0 z#*{hH9u)PqL4G!+e)un=(T>jKmF|GC-_MmU`w5_Ua7;GB1=(sTtTHhB9VBPSs?kFK61rxzr z#sLcI*<%0`D9FzMaqVeq8gvdqQS~uVG-?aw23g5~@}wQ=k9lkwYUS-a#MLY{-1?(7 zGId559@Z?Mc)-y`NzBwrF+ Date: Sat, 20 Mar 2021 11:47:22 +0100 Subject: [PATCH 51/67] feat: Touch support on selection --- samples/SharedDemo/Demos/Events.razor.cs | 25 +++++++++++++------ .../Behaviors/SelectionBehavior.cs | 16 ++++++++---- .../Components/Renderers/NodeRenderer.cs | 15 ++++++++--- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/samples/SharedDemo/Demos/Events.razor.cs b/samples/SharedDemo/Demos/Events.razor.cs index 6312a27a..8aed84be 100644 --- a/samples/SharedDemo/Demos/Events.razor.cs +++ b/samples/SharedDemo/Demos/Events.razor.cs @@ -42,21 +42,32 @@ private void RegisterEvents() diagram.Links.Added += (l) => events.Add($"Links.Added, LinkId={l.Id}"); - // Todo: replace with TargetPortChanged - //diagram.LinkAttached += (l) => - //{ - // events.Add($"LinkAttached, LinkId={l.Id}"); - // StateHasChanged(); - //}; - diagram.Links.Removed += (l) => events.Add($"Links.Removed, LinkId={l.Id}"); + diagram.MouseDown += (m, e) => + { + events.Add($"MouseDown, Type={m?.GetType().Name}, ModelId={m?.Id}"); + StateHasChanged(); + }; + diagram.MouseUp += (m, e) => { events.Add($"MouseUp, Type={m?.GetType().Name}, ModelId={m?.Id}"); StateHasChanged(); }; + diagram.TouchStart += (m, e) => + { + events.Add($"TouchStart, Type={m?.GetType().Name}, ModelId={m?.Id}"); + StateHasChanged(); + }; + + diagram.TouchEnd += (m, e) => + { + events.Add($"TouchEnd, Type={m?.GetType().Name}, ModelId={m?.Id}"); + StateHasChanged(); + }; + diagram.MouseClick += (m, e) => { events.Add($"MouseClick, Type={m?.GetType().Name}, ModelId={m?.Id}"); diff --git a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs index 13c54eda..40caf1d5 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs @@ -7,10 +7,15 @@ public class SelectionBehavior : Behavior { public SelectionBehavior(Diagram diagram) : base(diagram) { - Diagram.MouseDown += Diagram_MouseDown; + Diagram.MouseDown += OnMouseDown; + Diagram.TouchStart += OnTouchStart; } - private void Diagram_MouseDown(Model model, MouseEventArgs e) + private void OnTouchStart(Model model, TouchEventArgs e) => Process(model, e.CtrlKey); + + private void OnMouseDown(Model model, MouseEventArgs e) => Process(model, e.CtrlKey); + + private void Process(Model model, bool ctrlKey) { if (model == null) { @@ -18,20 +23,21 @@ private void Diagram_MouseDown(Model model, MouseEventArgs e) } else if (model is SelectableModel sm) { - if (e.CtrlKey && sm.Selected) + if (ctrlKey && sm.Selected) { Diagram.UnselectModel(sm); } else if (!sm.Selected) { - Diagram.SelectModel(sm, !e.CtrlKey || !Diagram.Options.AllowMultiSelection); + Diagram.SelectModel(sm, !ctrlKey || !Diagram.Options.AllowMultiSelection); } } } public override void Dispose() { - Diagram.MouseDown -= Diagram_MouseDown; + Diagram.MouseDown -= OnMouseDown; + Diagram.TouchStart -= OnTouchStart; } } } diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs index 1aa6217e..85f37a25 100644 --- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs @@ -107,9 +107,14 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.AddEventStopPropagationAttribute(5, "onmousedown", true); builder.AddAttribute(6, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); builder.AddEventStopPropagationAttribute(7, "onmouseup", true); - builder.AddElementReferenceCapture(8, value => _element = value); - builder.OpenComponent(9, componentType); - builder.AddAttribute(10, "Node", Node); + builder.AddAttribute(8, "ontouchstart", EventCallback.Factory.Create(this, OnTouchStart)); + builder.AddEventStopPropagationAttribute(9, "ontouchstart", true); + builder.AddAttribute(10, "ontouchend", EventCallback.Factory.Create(this, OnTouchEnd)); + builder.AddEventStopPropagationAttribute(11, "ontouchend", true); + builder.AddEventPreventDefaultAttribute(12, "ontouchend", true); + builder.AddElementReferenceCapture(13, value => _element = value); + builder.OpenComponent(14, componentType); + builder.AddAttribute(15, "Node", Node); builder.CloseComponent(); builder.CloseElement(); } @@ -165,5 +170,9 @@ private void ReRender() private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Node, e); private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Node, e); + + private void OnTouchStart(TouchEventArgs e) => Diagram.OnTouchStart(Node, e); + + private void OnTouchEnd(TouchEventArgs e) => Diagram.OnTouchEnd(Node, e); } } \ No newline at end of file From 6cc962650c3a5472e0fbb0618c640038080eb450 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 20 Mar 2021 13:00:31 +0100 Subject: [PATCH 52/67] feat: Touch events on links --- .../Components/Renderers/LinkRenderer.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs index c398296e..70804919 100644 --- a/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs +++ b/src/Blazor.Diagrams/Components/Renderers/LinkRenderer.cs @@ -44,8 +44,13 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) builder.AddEventStopPropagationAttribute(4, "onmousedown", true); builder.AddAttribute(5, "onmouseup", EventCallback.Factory.Create(this, OnMouseUp)); builder.AddEventStopPropagationAttribute(6, "onmouseup", true); - builder.OpenComponent(7, componentType); - builder.AddAttribute(8, "Link", Link); + builder.AddAttribute(7, "ontouchstart", EventCallback.Factory.Create(this, OnTouchStart)); + builder.AddEventStopPropagationAttribute(8, "ontouchstart", true); + builder.AddAttribute(9, "ontouchend", EventCallback.Factory.Create(this, OnTouchEnd)); + builder.AddEventStopPropagationAttribute(10, "ontouchend", true); + builder.AddEventPreventDefaultAttribute(11, "ontouchend", true); + builder.OpenComponent(12, componentType); + builder.AddAttribute(13, "Link", Link); builder.CloseComponent(); builder.CloseElement(); } @@ -61,5 +66,9 @@ private void Link_Changed() private void OnMouseDown(MouseEventArgs e) => Diagram.OnMouseDown(Link, e); private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Link, e); + + private void OnTouchStart(TouchEventArgs e) => Diagram.OnTouchStart(Link, e); + + private void OnTouchEnd(TouchEventArgs e) => Diagram.OnTouchEnd(Link, e); } } From ba8916db8ef2da3057ac334e1f13abc37b41127a Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 20 Mar 2021 20:22:43 +0100 Subject: [PATCH 53/67] feat: Touch support for dragging movables --- .../Behaviors/DragMovablesBehavior.cs | 58 +++++++++++++------ .../Behaviors/PanBehavior.cs | 55 +++++++++++++----- .../Components/DiagramCanvas.razor | 5 +- .../Components/DiagramCanvas.razor.cs | 4 ++ .../Components/LinkVertexWidget.razor | 7 ++- .../Components/LinkVertexWidget.razor.cs | 4 ++ .../Components/LinkWidget.razor | 4 +- .../Components/LinkWidget.razor.cs | 19 +++++- 8 files changed, 117 insertions(+), 39 deletions(-) diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs index 667eaa13..3e67dcbd 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs @@ -14,12 +14,29 @@ public class DragMovablesBehavior : Behavior public DragMovablesBehavior(Diagram diagram) : base(diagram) { - Diagram.MouseDown += Diagram_MouseDown; - Diagram.MouseMove += Diagram_MouseMove; - Diagram.MouseUp += Diagram_MouseUp; + Diagram.MouseDown += OnMouseDown; + Diagram.MouseMove += OnMouseMove; + Diagram.MouseUp += OnMouseUp; + Diagram.TouchStart += OnTouchStart; + Diagram.TouchMove += OnTouchMove; + Diagram.TouchEnd += OnTouchEnd; } - private void Diagram_MouseDown(Model model, MouseEventArgs e) + private void OnTouchStart(Model model, TouchEventArgs e) + => Start(model, e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY); + + private void OnTouchMove(Model model, TouchEventArgs e) + => Move(e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY); + + private void OnTouchEnd(Model model, TouchEventArgs e) => End(); + + private void OnMouseDown(Model model, MouseEventArgs e) => Start(model, e.ClientX, e.ClientY); + + private void OnMouseMove(Model model, MouseEventArgs e) => Move(e.ClientX, e.ClientY); + + private void OnMouseUp(Model model, MouseEventArgs e) => End(); + + private void Start(Model model, double clientX, double clientY) { if (!(model is MovableModel)) return; @@ -30,17 +47,17 @@ private void Diagram_MouseDown(Model model, MouseEventArgs e) .Select(m => (m as MovableModel)!.Position) .ToArray(); - _lastClientX = e.ClientX; - _lastClientY = e.ClientY; + _lastClientX = clientX; + _lastClientY = clientY; } - private void Diagram_MouseMove(Model model, MouseEventArgs e) + private void Move(double clientX, double clientY) { if (_initialPositions == null || _lastClientX == null || _lastClientY == null) return; - var deltaX = (e.ClientX - _lastClientX.Value) / Diagram.Zoom; - var deltaY = (e.ClientY - _lastClientY.Value) / Diagram.Zoom; + var deltaX = (clientX - _lastClientX.Value) / Diagram.Zoom; + var deltaY = (clientY - _lastClientY.Value) / Diagram.Zoom; var i = 0; foreach (var sm in Diagram.GetSelectedModels()) @@ -56,6 +73,13 @@ private void Diagram_MouseMove(Model model, MouseEventArgs e) } } + private void End() + { + _initialPositions = null; + _lastClientX = null; + _lastClientY = null; + } + private double ApplyGridSize(double n) { if (Diagram.Options.GridSize == null) @@ -69,18 +93,14 @@ private double ApplyGridSize(double n) return gridSize * Math.Floor((n + gridSize / 2) / gridSize); } - private void Diagram_MouseUp(Model model, MouseEventArgs e) - { - _initialPositions = null; - _lastClientX = null; - _lastClientY = null; - } - public override void Dispose() { - Diagram.MouseDown -= Diagram_MouseDown; - Diagram.MouseMove -= Diagram_MouseMove; - Diagram.MouseUp -= Diagram_MouseUp; + Diagram.MouseDown -= OnMouseDown; + Diagram.MouseMove -= OnMouseMove; + Diagram.MouseUp -= OnMouseUp; + Diagram.TouchStart -= OnTouchStart; + Diagram.TouchMove -= OnTouchMove; + Diagram.TouchEnd -= OnTouchEnd; } } } diff --git a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs index 670d4784..4cdbec41 100644 --- a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs +++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs @@ -1,7 +1,6 @@ using Blazor.Diagrams.Core.Geometry; using Blazor.Diagrams.Core.Models.Base; using Microsoft.AspNetCore.Components.Web; -using System; namespace Blazor.Diagrams.Core.Behaviors { @@ -13,32 +12,55 @@ public class PanBehavior : Behavior public PanBehavior(Diagram diagram) : base(diagram) { - Diagram.MouseDown += Diagram_MouseDown; - Diagram.MouseMove += Diagram_MouseMove; - Diagram.MouseUp += Diagram_MouseUp; + Diagram.MouseDown += OnMouseDown; + Diagram.MouseMove += OnMouseMove; + Diagram.MouseUp += OnMouseUp; + Diagram.TouchStart += OnTouchStart; + Diagram.TouchMove += OnTouchmove; + Diagram.TouchEnd += OnTouchEnd; } - private void Diagram_MouseDown(Model model, MouseEventArgs e) + private void OnTouchStart(Model model, TouchEventArgs e) + => Start(model, e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY, e.ShiftKey); + + private void OnTouchmove(Model model, TouchEventArgs e) + => Move(e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY); + + private void OnTouchEnd(Model model, TouchEventArgs e) => End(); + + private void OnMouseDown(Model model, MouseEventArgs e) + { + if (e.Button != (int)MouseEventButton.Left) + return; + + Start(model, e.ClientX, e.ClientY, e.ShiftKey); + } + + private void OnMouseMove(Model model, MouseEventArgs e) => Move(e.ClientX, e.ClientY); + + private void OnMouseUp(Model model, MouseEventArgs e) => End(); + + private void Start(Model model, double clientX, double clientY, bool shiftKey) { - if (!Diagram.Options.AllowPanning || model != null || e.ShiftKey || e.Button != (int)MouseEventButton.Left) + if (!Diagram.Options.AllowPanning || model != null || shiftKey) return; _initialPan = Diagram.Pan; - _lastClientX = e.ClientX; - _lastClientY = e.ClientY; + _lastClientX = clientX; + _lastClientY = clientY; } - private void Diagram_MouseMove(Model model, MouseEventArgs e) + private void Move(double clientX, double clientY) { if (!Diagram.Options.AllowPanning || _initialPan == null) return; - var deltaX = e.ClientX - _lastClientX - (Diagram.Pan.X - _initialPan.X); - var deltaY = e.ClientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y); + var deltaX = clientX - _lastClientX - (Diagram.Pan.X - _initialPan.X); + var deltaY = clientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y); Diagram.UpdatePan(deltaX, deltaY); } - private void Diagram_MouseUp(Model model, MouseEventArgs e) + private void End() { if (!Diagram.Options.AllowPanning) return; @@ -48,9 +70,12 @@ private void Diagram_MouseUp(Model model, MouseEventArgs e) public override void Dispose() { - Diagram.MouseDown -= Diagram_MouseDown; - Diagram.MouseMove -= Diagram_MouseMove; - Diagram.MouseUp -= Diagram_MouseUp; + Diagram.MouseDown -= OnMouseDown; + Diagram.MouseMove -= OnMouseMove; + Diagram.MouseUp -= OnMouseUp; + Diagram.TouchStart -= OnTouchStart; + Diagram.TouchMove -= OnTouchmove; + Diagram.TouchEnd -= OnTouchEnd; } } } diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor b/src/Blazor.Diagrams/Components/DiagramCanvas.razor index 878ed486..3636d074 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor @@ -8,7 +8,10 @@ @onkeydown="OnKeyDown" @onwheel="OnWheel" @onwheel:stopPropagation - @ontouchmove="OnTouchMove"> + @ontouchstart="OnTouchStart" + @ontouchmove="OnTouchMove" + @ontouchend="OnTouchEnd" + @ontouchend:preventDefault> @* Links *@ diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs index 2e00d75b..f6b6c138 100644 --- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs +++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs @@ -73,8 +73,12 @@ protected override bool ShouldRender() private void OnWheel(WheelEventArgs e) => Diagram.OnWheel(e); + private void OnTouchStart(TouchEventArgs e) => Diagram.OnTouchStart(null, e); + private void OnTouchMove(TouchEventArgs e) => Diagram.OnTouchMove(null, e); + private void OnTouchEnd(TouchEventArgs e) => Diagram.OnTouchEnd(null, e); + private void OnDiagramChanged() { _shouldReRender = true; diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor index 762f909d..bebfd820 100644 --- a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor @@ -7,4 +7,9 @@ @onmousedown="OnMouseDown" @onmousedown:stopPropagation @onmouseup="OnMouseUp" - @onmouseup:stopPropagation /> \ No newline at end of file + @onmouseup:stopPropagation + @ontouchstart="OnTouchStart" + @ontouchstart:stopPropagation + @ontouchend="OnTouchEnd" + @ontouchend:preventDefault + @ontouchend:stopPropagation /> \ No newline at end of file diff --git a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs index cd7fff8e..6eada93e 100644 --- a/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkVertexWidget.razor.cs @@ -48,6 +48,10 @@ private void OnVertexChanged() private void OnMouseUp(MouseEventArgs e) => Diagram.OnMouseUp(Vertex, e); + private void OnTouchStart(TouchEventArgs e) => Diagram.OnTouchStart(Vertex, e); + + private void OnTouchEnd(TouchEventArgs e) => Diagram.OnTouchEnd(Vertex, e); + private void OnDoubleClick(MouseEventArgs e) { Vertex.Parent.Vertices.Remove(Vertex); diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor b/src/Blazor.Diagrams/Components/LinkWidget.razor index 791102b7..808771b7 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor @@ -32,7 +32,9 @@ stroke-opacity="0" fill="none" @onmousedown="e => OnMouseDown(e, index)" - @onmousedown:stopPropagation="@Link.Segmentable" /> + @onmousedown:stopPropagation="@Link.Segmentable" + @ontouchstart="e => OnTouchStart(e, index)" + @ontouchstart:stopPropagation="@Link.Segmentable"/> } } diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index 3f8a3e55..5af60cf1 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -20,10 +20,25 @@ private void OnMouseDown(MouseEventArgs e, int index) if (!Link.Segmentable) return; - var rPt = Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY); + var vertex = CreateVertex(e.ClientX, e.ClientY, index); + Diagram.OnMouseDown(vertex, e); + } + + private void OnTouchStart(TouchEventArgs e, int index) + { + if (!Link.Segmentable) + return; + + var vertex = CreateVertex(e.ChangedTouches[0].ClientX, e.ChangedTouches[0].ClientY, index); + Diagram.OnTouchStart(vertex, e); + } + + private LinkVertexModel CreateVertex(double clientX, double clientY, int index) + { + var rPt = Diagram.GetRelativeMousePoint(clientX, clientY); var vertex = new LinkVertexModel(Link, rPt); Link.Vertices.Insert(index, vertex); - Diagram.OnMouseDown(vertex, e); + return vertex; } private (Point source, Point target) FindConnectionPoints(Point[] route) From d2d0c9f2141db635a449604cf862495cf585275a Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sat, 20 Mar 2021 20:51:21 +0100 Subject: [PATCH 54/67] fix: Useless refreshes when Container values didn't change --- src/Blazor.Diagrams.Core/Diagram.cs | 3 +++ src/Blazor.Diagrams.Core/Geometry/Rectangle.cs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs index 4aacd688..9e38d605 100644 --- a/src/Blazor.Diagrams.Core/Diagram.cs +++ b/src/Blazor.Diagrams.Core/Diagram.cs @@ -306,6 +306,9 @@ public void SetZoom(double newZoom) public void SetContainer(Rectangle newRect) { + if (newRect.Equals(Container)) + return; + Container = newRect; ContainerChanged?.Invoke(); Refresh(); diff --git a/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs b/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs index e0db2825..25068f4f 100644 --- a/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs +++ b/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs @@ -99,6 +99,12 @@ public IEnumerable GetIntersectionsWithLine(Line line) public Point South => new Point(Left + Width / 2, Bottom); public Point West => new Point(Left, Top + Height / 2); + public bool Equals(Rectangle? other) + { + return other != null && Left == other.Left && Right == other.Right && Top == other.Top && + Bottom == other.Bottom && Width == other.Width && Height == other.Height; + } + public override string ToString() => $"Rectangle(width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})"; } From b98594b2ddd4a467f93b7f1122bbc3152e07d5fa Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sun, 21 Mar 2021 19:56:02 +0100 Subject: [PATCH 55/67] tests: Extension methods --- Blazor.Diagrams.sln | 9 ++++ .../Extensions/EnumerableExtensions.cs | 17 ------- .../Blazor.Diagrams.Core.Tests.csproj | 26 ++++++++++ .../Extensions/DiagramExtensionsTests.cs | 49 +++++++++++++++++++ .../Extensions/DoubleExtensionsTests.cs | 21 ++++++++ 5 files changed, 105 insertions(+), 17 deletions(-) delete mode 100644 src/Blazor.Diagrams.Core/Extensions/EnumerableExtensions.cs create mode 100644 tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj create mode 100644 tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs create mode 100644 tests/Blazor.Diagrams.Core.Tests/Extensions/DoubleExtensionsTests.cs diff --git a/Blazor.Diagrams.sln b/Blazor.Diagrams.sln index ece4a9b9..f1876bf9 100644 --- a/Blazor.Diagrams.sln +++ b/Blazor.Diagrams.sln @@ -25,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{CEEAE4C2-CE68-4FC3-9E0F-D4781B91F7F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Diagrams.Core.Tests", "tests\Blazor.Diagrams.Core.Tests\Blazor.Diagrams.Core.Tests.csproj", "{36B4DCCD-45AB-4338-9224-DDAF386A23A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -55,6 +59,10 @@ Global {CB3A42B6-3C87-4ECB-B60C-D98275AB1FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB3A42B6-3C87-4ECB-B60C-D98275AB1FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB3A42B6-3C87-4ECB-B60C-D98275AB1FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {36B4DCCD-45AB-4338-9224-DDAF386A23A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36B4DCCD-45AB-4338-9224-DDAF386A23A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36B4DCCD-45AB-4338-9224-DDAF386A23A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36B4DCCD-45AB-4338-9224-DDAF386A23A3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -66,6 +74,7 @@ Global {1CBCC8E6-111C-4364-9882-50881E4CC8B4} = {DA819127-3EF6-4EB9-A2DA-BC056B284A50} {B9EE910B-8FE7-490C-B20C-CEC27A2890D6} = {DA819127-3EF6-4EB9-A2DA-BC056B284A50} {CB3A42B6-3C87-4ECB-B60C-D98275AB1FB6} = {EE32E278-A887-454E-987D-FFE9E37169FE} + {36B4DCCD-45AB-4338-9224-DDAF386A23A3} = {CEEAE4C2-CE68-4FC3-9E0F-D4781B91F7F4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {969540A2-8162-4063-A4E3-B488F69BD582} diff --git a/src/Blazor.Diagrams.Core/Extensions/EnumerableExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/EnumerableExtensions.cs deleted file mode 100644 index 999968ec..00000000 --- a/src/Blazor.Diagrams.Core/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace Blazor.Diagrams.Core.Extensions -{ - public static class EnumerableExtensions - { - public static IEnumerable<(int index, T element)> LoopWithIndex(this IEnumerable enumerable) - { - var i = 0; - foreach (var element in enumerable) - { - yield return (i, element); - i++; - } - } - } -} diff --git a/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj b/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj new file mode 100644 index 00000000..8c2cf5ad --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp3.1 + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs b/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs new file mode 100644 index 00000000..3ecc98a6 --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs @@ -0,0 +1,49 @@ +using Blazor.Diagrams.Core.Extensions; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; +using Xunit; + +namespace Blazor.Diagrams.Core.Tests.Extensions +{ + public class DiagramExtensionsTests + { + [Fact] + public void GetBounds_ShouldReturnZeroRectangle_WhenNodesAreEmpty() + { + // Arrange + var nodes = new NodeModel[0]; + + // Act + var bounds = nodes.GetBounds(); + + // Assert + Assert.True(Rectangle.Zero.Equals(bounds)); + } + + [Fact] + public void GetBounds_ShouldReturnCorrectBounds() + { + // Arrange + var nodes = new NodeModel[] + { + new NodeModel + { + Position = new Point(10, 10), + Size = new Size(100, 100) + }, + new NodeModel + { + Position = new Point(200, 200), + Size = new Size(100, 100) + }, + }; + + // Act + var bounds = nodes.GetBounds(); + + // Assert + var expected = new Rectangle(10, 10, 300, 300); + Assert.True(expected.Equals(bounds)); + } + } +} diff --git a/tests/Blazor.Diagrams.Core.Tests/Extensions/DoubleExtensionsTests.cs b/tests/Blazor.Diagrams.Core.Tests/Extensions/DoubleExtensionsTests.cs new file mode 100644 index 00000000..c454a415 --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Extensions/DoubleExtensionsTests.cs @@ -0,0 +1,21 @@ +using Xunit; +using Blazor.Diagrams.Core.Extensions; + +namespace Blazor.Diagrams.Core.Tests.Extensions +{ + public class DoubleExtensionsTests + { + [Theory] + [InlineData(5, 10, 0.1, false)] + [InlineData(1.1, 1.2, 0.01, false)] + [InlineData(10, 10, 0.0001, true)] + [InlineData(10.35, 10.35, 0.0001, true)] + [InlineData(1.659, 1.660, 0.0001, false)] + [InlineData(1.65999, 1.65998, 0.0001, true)] + [InlineData(1.65999, 1.6599998, 0.0001, true)] + public void AlmostEqualTo(double num1, double num2, double tolerance, bool expected) + { + Assert.Equal(expected, num1.AlmostEqualTo(num2, tolerance)); + } + } +} From 552398aa31ea893adad58b41cad766fca45e5e8a Mon Sep 17 00:00:00 2001 From: zHaytam Date: Sun, 21 Mar 2021 20:24:45 +0100 Subject: [PATCH 56/67] tests: Add Point.DistanceTo tests --- .../Blazor.Diagrams.Core.Tests.csproj | 1 + .../Geometry/PointTests.cs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs diff --git a/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj b/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj index 8c2cf5ad..4f95ed61 100644 --- a/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj +++ b/tests/Blazor.Diagrams.Core.Tests/Blazor.Diagrams.Core.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs b/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs new file mode 100644 index 00000000..ab15e59a --- /dev/null +++ b/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs @@ -0,0 +1,21 @@ +using Blazor.Diagrams.Core.Geometry; +using FluentAssertions; +using Xunit; + +namespace Blazor.Diagrams.Core.Tests.Geometry +{ + public class PointTests + { + [Theory] + [InlineData(0, 0, 0, 0, 0)] + [InlineData(-7, -4, 17, 6.5, 26.196374)] + [InlineData(5, 10, 33, 98, 92.347171)] + [InlineData(5.5, 2.7, 6.5, 47.2, 44.511235)] + public void DistanceTo(double x1, double y1, double x2, double y2, double expected) + { + var pt1 = new Point(x1, y1); + var pt2 = new Point(x2, y2); + pt1.DistanceTo(pt2).Should().BeApproximately(expected, 0.0001); + } + } +} From 5d19b76541fdf71d661aedab07f7729892a7734a Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 22 Mar 2021 11:22:46 +0100 Subject: [PATCH 57/67] fix: GitHub Icons in Index page --- samples/SharedDemo/Home.razor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/SharedDemo/Home.razor b/samples/SharedDemo/Home.razor index 2376b2b9..293bc287 100644 --- a/samples/SharedDemo/Home.razor +++ b/samples/SharedDemo/Home.razor @@ -18,11 +18,11 @@ From dc8f9c645689cc7cfbf817bba231bbd6ed3d6cfa Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 22 Mar 2021 15:22:14 +0100 Subject: [PATCH 58/67] fix: markers wrong position --- Blazor.Diagrams.sln | 2 +- .../Routers/Routers.Orthogonal.cs | 9 +----- .../Components/LinkWidget.razor.cs | 31 ++++++++++++++++++- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Blazor.Diagrams.sln b/Blazor.Diagrams.sln index f1876bf9..520964b5 100644 --- a/Blazor.Diagrams.sln +++ b/Blazor.Diagrams.sln @@ -27,7 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{CEEAE4C2-CE68-4FC3-9E0F-D4781B91F7F4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.Diagrams.Core.Tests", "tests\Blazor.Diagrams.Core.Tests\Blazor.Diagrams.Core.Tests.csproj", "{36B4DCCD-45AB-4338-9224-DDAF386A23A3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.Diagrams.Core.Tests", "tests\Blazor.Diagrams.Core.Tests\Blazor.Diagrams.Core.Tests.csproj", "{36B4DCCD-45AB-4338-9224-DDAF386A23A3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs b/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs index a0d89102..3ccedbc4 100644 --- a/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs +++ b/src/Blazor.Diagrams.Core/Routers/Routers.Orthogonal.cs @@ -83,17 +83,10 @@ public static Point[] Orthogonal(Diagram _, BaseLinkModel link) var origin = ExtrudeCp(originA, shapeMargin, sideA); var destination = ExtrudeCp(originB, shapeMargin, sideB); - var start = originA; - var end = originB; - var path = ShortestPath(graph, origin, destination); if (path.Length > 0) { - var result = new List(); - result.Add(start); - result.AddRange(SimplifyPath(path)); - result.Add(end); - return result.ToArray(); + return SimplifyPath(path); } else { diff --git a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs index 5af60cf1..7eae007d 100644 --- a/src/Blazor.Diagrams/Components/LinkWidget.razor.cs +++ b/src/Blazor.Diagrams/Components/LinkWidget.razor.cs @@ -65,7 +65,36 @@ private LinkVertexModel CreateVertex(double clientX, double clientY, int index) if (!Link.SourcePort.Initialized || Link.TargetPort?.Initialized == false) return (null, null); - return (Link.SourcePort.MiddlePosition, Link.TargetPort?.MiddlePosition ?? Link.OnGoingPosition); + var source = GetPortPositionBasedOnAlignment(Link.SourcePort, Link.SourceMarker); + var target = GetPortPositionBasedOnAlignment(Link.TargetPort, Link.TargetMarker); + return (source, target ?? Link.OnGoingPosition); + } + } + + private Point GetPortPositionBasedOnAlignment(PortModel port, LinkMarker marker) + { + if (marker == null) + return port.MiddlePosition; + + var pt = port.Position; + switch (port.Alignment) + { + case PortAlignment.Top: + return new Point(pt.X + port.Size.Width / 2, pt.Y); + case PortAlignment.TopRight: + return new Point(pt.X + port.Size.Width, pt.Y); + case PortAlignment.Right: + return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height / 2); + case PortAlignment.BottomRight: + return new Point(pt.X + port.Size.Width, pt.Y + port.Size.Height); + case PortAlignment.Bottom: + return new Point(pt.X + port.Size.Width / 2, pt.Y + port.Size.Height); + case PortAlignment.BottomLeft: + return new Point(pt.X, pt.Y + port.Size.Height); + case PortAlignment.Left: + return new Point(pt.X, pt.Y + port.Size.Height / 2); + default: + return pt; } } From 0d4380a3400c248838bb40785ca3038abeba9795 Mon Sep 17 00:00:00 2001 From: zHaytam Date: Mon, 22 Mar 2021 16:54:22 +0100 Subject: [PATCH 59/67] docs: Links --- samples/SharedDemo/Components/DemoMenu.razor | 34 +++++++ .../SharedDemo/Demos/Links/LabelsDemo.razor | 7 ++ .../Demos/Links/LabelsDemo.razor.cs | 79 ++++++++++++++++ .../SharedDemo/Demos/Links/MarkersDemo.razor | 7 ++ .../Demos/Links/MarkersDemo.razor.cs | 90 +++++++++++++++++++ .../Demos/Links/PathGeneratorsDemo.razor | 7 ++ .../Demos/Links/PathGeneratorsDemo.razor.cs | 58 ++++++++++++ .../SharedDemo/Demos/Links/RoutersDemo.razor | 7 ++ .../Demos/Links/RoutersDemo.razor.cs | 57 ++++++++++++ .../SharedDemo/Demos/Links/VerticesDemo.razor | 7 ++ .../Demos/Links/VerticesDemo.razor.cs | 62 +++++++++++++ samples/SharedDemo/Demos/Simple.razor.cs | 17 +++- samples/SharedDemo/Layouts/DemoLayout.razor | 67 +++++++++----- samples/SharedDemo/_Imports.razor | 3 +- 14 files changed, 479 insertions(+), 23 deletions(-) create mode 100644 samples/SharedDemo/Components/DemoMenu.razor create mode 100644 samples/SharedDemo/Demos/Links/LabelsDemo.razor create mode 100644 samples/SharedDemo/Demos/Links/LabelsDemo.razor.cs create mode 100644 samples/SharedDemo/Demos/Links/MarkersDemo.razor create mode 100644 samples/SharedDemo/Demos/Links/MarkersDemo.razor.cs create mode 100644 samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor create mode 100644 samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor.cs create mode 100644 samples/SharedDemo/Demos/Links/RoutersDemo.razor create mode 100644 samples/SharedDemo/Demos/Links/RoutersDemo.razor.cs create mode 100644 samples/SharedDemo/Demos/Links/VerticesDemo.razor create mode 100644 samples/SharedDemo/Demos/Links/VerticesDemo.razor.cs diff --git a/samples/SharedDemo/Components/DemoMenu.razor b/samples/SharedDemo/Components/DemoMenu.razor new file mode 100644 index 00000000..24957ea0 --- /dev/null +++ b/samples/SharedDemo/Components/DemoMenu.razor @@ -0,0 +1,34 @@ +
  • + + @Text + @if (ChildContent != null) + { + + } + + + @if (ChildContent != null && !Collapsed) + { +
      + @ChildContent +
    + } +
  • + +@code { + [Parameter] public string Href { get; set; } = "#"; + [Parameter] public string Text { get; set; } + [Parameter] public RenderFragment ChildContent { get; set; } + public bool Collapsed { get; set; } = true; + + private void OnItemClicked() + { + if (ChildContent == null) + return; + + Collapsed = !Collapsed; + } +} \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Links/LabelsDemo.razor b/samples/SharedDemo/Demos/Links/LabelsDemo.razor new file mode 100644 index 00000000..fd892997 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/LabelsDemo.razor @@ -0,0 +1,7 @@ +@page "/links/labels" +@layout DemoLayout +@inject LayoutData LayoutData + + + + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Links/LabelsDemo.razor.cs b/samples/SharedDemo/Demos/Links/LabelsDemo.razor.cs new file mode 100644 index 00000000..b824fc5e --- /dev/null +++ b/samples/SharedDemo/Demos/Links/LabelsDemo.razor.cs @@ -0,0 +1,79 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; +using Blazor.Diagrams.Core.Models.Base; +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharedDemo.Demos.Links +{ + public partial class LabelsDemo + { + private Diagram _diagram = new Diagram(); + + protected override void OnInitialized() + { + base.OnInitialized(); + + LayoutData.Title = "Link Labels"; + LayoutData.Info = "Labels help you show more information through out a link. You can specify a distance or an offset.
    " + + "The content of the labels is still limited because of Blazor's poor SVG support."; + LayoutData.DataChanged(); + + InitializeDiagram(); + } + + private void InitializeDiagram() + { + var node1 = NewNode(50, 50); + var node2 = NewNode(400, 50); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + var link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.Labels.Add(new LinkLabelModel(link, "Content")); + _diagram.Links.Add(link); + + node1 = NewNode(50, 160); + node2 = NewNode(400, 160); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.Labels.Add(new LinkLabelModel(link, "0.25", 0.3)); + link.Labels.Add(new LinkLabelModel(link, "0.75", 0.7)); + _diagram.Links.Add(link); + + node1 = NewNode(50, 270); + node2 = NewNode(400, 270); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.Labels.Add(new LinkLabelModel(link, "50", 50)); + link.Labels.Add(new LinkLabelModel(link, "-50", -50)); + _diagram.Links.Add(link); + + node1 = NewNode(50, 380); + node2 = NewNode(400, 380); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.Labels.Add(new LinkLabelModel(link, "(0,-20)", 50, new Point(0, -20))); + link.Labels.Add(new LinkLabelModel(link, "(0,20)", -50, new Point(0, 20))); + _diagram.Links.Add(link); + } + + private NodeModel NewNode(double x, double y) + { + var node = new NodeModel(new Point(x, y)); + node.AddPort(PortAlignment.Bottom); + node.AddPort(PortAlignment.Top); + node.AddPort(PortAlignment.Left); + node.AddPort(PortAlignment.Right); + return node; + } + } +} diff --git a/samples/SharedDemo/Demos/Links/MarkersDemo.razor b/samples/SharedDemo/Demos/Links/MarkersDemo.razor new file mode 100644 index 00000000..37e4b943 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/MarkersDemo.razor @@ -0,0 +1,7 @@ +@page "/links/markers" +@layout DemoLayout +@inject LayoutData LayoutData + + + + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Links/MarkersDemo.razor.cs b/samples/SharedDemo/Demos/Links/MarkersDemo.razor.cs new file mode 100644 index 00000000..2de943cd --- /dev/null +++ b/samples/SharedDemo/Demos/Links/MarkersDemo.razor.cs @@ -0,0 +1,90 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; + +namespace SharedDemo.Demos.Links +{ + public partial class MarkersDemo + { + private Diagram _diagram = new Diagram(); + + protected override void OnInitialized() + { + base.OnInitialized(); + + LayoutData.Title = "Link Markers"; + LayoutData.Info = "Markers are SVG Paths that you can put at the beginning or at the end of your links."; + LayoutData.DataChanged(); + + InitializeDiagram(); + } + + private void InitializeDiagram() + { + var node1 = NewNode(50, 50); + var node2 = NewNode(400, 50); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + var link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.SourceMarker = LinkMarker.Arrow; + link.TargetMarker = LinkMarker.Arrow; + link.Labels.Add(new LinkLabelModel(link, "Arrow")); + _diagram.Links.Add(link); + + node1 = NewNode(50, 160); + node2 = NewNode(400, 160); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.SourceMarker = LinkMarker.Circle; + link.TargetMarker = LinkMarker.Circle; + link.Labels.Add(new LinkLabelModel(link, "Circle")); + _diagram.Links.Add(link); + + node1 = NewNode(50, 270); + node2 = NewNode(400, 270); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.SourceMarker = LinkMarker.Square; + link.TargetMarker = LinkMarker.Square; + link.Labels.Add(new LinkLabelModel(link, "Square")); + _diagram.Links.Add(link); + + node1 = NewNode(50, 380); + node2 = NewNode(400, 380); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.SourceMarker = LinkMarker.NewRectangle(10, 20); + link.TargetMarker = LinkMarker.NewArrow(20, 10); + link.Labels.Add(new LinkLabelModel(link, "Factory")); + _diagram.Links.Add(link); + + node1 = NewNode(50, 490); + node2 = NewNode(400, 490); + + _diagram.Nodes.Add(new[] { node1, node2 }); + + link = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)); + link.SourceMarker = new LinkMarker("M 0 -8 L 3 -8 3 8 0 8 z M 4 -8 7 -8 7 8 4 8 z M 8 -8 16 0 8 8 z", 16); + link.TargetMarker = new LinkMarker("M 0 -8 L 8 -8 4 0 8 8 0 8 4 0 z", 8); + link.Labels.Add(new LinkLabelModel(link, "Custom")); + _diagram.Links.Add(link); + } + + private NodeModel NewNode(double x, double y) + { + var node = new NodeModel(new Point(x, y)); + node.AddPort(PortAlignment.Bottom); + node.AddPort(PortAlignment.Top); + node.AddPort(PortAlignment.Left); + node.AddPort(PortAlignment.Right); + return node; + } + } +} diff --git a/samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor b/samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor new file mode 100644 index 00000000..9b0e08c8 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor @@ -0,0 +1,7 @@ +@page "/links/path-generators" +@layout DemoLayout +@inject LayoutData LayoutData + + + + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor.cs b/samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor.cs new file mode 100644 index 00000000..1e56a2b6 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/PathGeneratorsDemo.razor.cs @@ -0,0 +1,58 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; + +namespace SharedDemo.Demos.Links +{ + public partial class PathGeneratorsDemo + { + private Diagram _diagram = new Diagram(); + + protected override void OnInitialized() + { + base.OnInitialized(); + + LayoutData.Title = "Link Path Generators"; + LayoutData.Info = "Path generators are functions that take as input the calculated route and output SVG paths, " + + "alongside the markers positions and their angles. There are currently two generators: Straight and Smooth."; + LayoutData.DataChanged(); + + InitializeDiagram(); + } + + private void InitializeDiagram() + { + var node1 = NewNode(50, 80); + var node2 = NewNode(300, 350); + var node3 = NewNode(400, 100); + + _diagram.Nodes.Add(new[] { node1, node2, node3 }); + + var link1 = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) + { + Router = Routers.Normal, + PathGenerator = PathGenerators.Straight + }; + link1.Labels.Add(new LinkLabelModel(link1, "Straight")); + + var link2 = new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)) + { + Router = Routers.Normal, + PathGenerator = PathGenerators.Smooth + }; + link2.Labels.Add(new LinkLabelModel(link2, "Smooth")); + + _diagram.Links.Add(new[] { link1, link2 }); + } + + private NodeModel NewNode(double x, double y) + { + var node = new NodeModel(new Point(x, y)); + node.AddPort(PortAlignment.Bottom); + node.AddPort(PortAlignment.Top); + node.AddPort(PortAlignment.Left); + node.AddPort(PortAlignment.Right); + return node; + } + } +} diff --git a/samples/SharedDemo/Demos/Links/RoutersDemo.razor b/samples/SharedDemo/Demos/Links/RoutersDemo.razor new file mode 100644 index 00000000..c64a33dc --- /dev/null +++ b/samples/SharedDemo/Demos/Links/RoutersDemo.razor @@ -0,0 +1,7 @@ +@page "/links/routers" +@layout DemoLayout +@inject LayoutData LayoutData + + + + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Links/RoutersDemo.razor.cs b/samples/SharedDemo/Demos/Links/RoutersDemo.razor.cs new file mode 100644 index 00000000..8700d498 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/RoutersDemo.razor.cs @@ -0,0 +1,57 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; + +namespace SharedDemo.Demos.Links +{ + public partial class RoutersDemo + { + private Diagram _diagram = new Diagram(); + + protected override void OnInitialized() + { + base.OnInitialized(); + + LayoutData.Title = "Link Routers"; + LayoutData.Info = "Routers are functions that take as input the link's vertices and can add points in between. " + + "There are currently two routers: Normal and Orthogonal."; + LayoutData.DataChanged(); + + InitializeDiagram(); + } + + private void InitializeDiagram() + { + var node1 = NewNode(50, 80); + var node2 = NewNode(300, 350); + var node3 = NewNode(400, 100); + + _diagram.Nodes.Add(new[] { node1, node2, node3 }); + + var link1 = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) + { + Router = Routers.Normal + }; + link1.Labels.Add(new LinkLabelModel(link1, "Normal")); + + var link2 = new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)) + { + Router = Routers.Orthogonal, + PathGenerator = PathGenerators.Straight // Smooth results in weird looking links + }; + link2.Labels.Add(new LinkLabelModel(link2, "Orthogonal")); + + _diagram.Links.Add(new[] { link1, link2 }); + } + + private NodeModel NewNode(double x, double y) + { + var node = new NodeModel(new Point(x, y)); + node.AddPort(PortAlignment.Bottom); + node.AddPort(PortAlignment.Top); + node.AddPort(PortAlignment.Left); + node.AddPort(PortAlignment.Right); + return node; + } + } +} diff --git a/samples/SharedDemo/Demos/Links/VerticesDemo.razor b/samples/SharedDemo/Demos/Links/VerticesDemo.razor new file mode 100644 index 00000000..977f3512 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/VerticesDemo.razor @@ -0,0 +1,7 @@ +@page "/links/vertices" +@layout DemoLayout +@inject LayoutData LayoutData + + + + \ No newline at end of file diff --git a/samples/SharedDemo/Demos/Links/VerticesDemo.razor.cs b/samples/SharedDemo/Demos/Links/VerticesDemo.razor.cs new file mode 100644 index 00000000..d51baac5 --- /dev/null +++ b/samples/SharedDemo/Demos/Links/VerticesDemo.razor.cs @@ -0,0 +1,62 @@ +using Blazor.Diagrams.Core; +using Blazor.Diagrams.Core.Geometry; +using Blazor.Diagrams.Core.Models; + +namespace SharedDemo.Demos.Links +{ + public partial class VerticesDemo + { + private Diagram _diagram = new Diagram(); + + protected override void OnInitialized() + { + base.OnInitialized(); + + LayoutData.Title = "Link Vertices"; + LayoutData.Info = "Click on a link to create a vertex. Double click on a vertex to delete it. " + + "You can drag the vertices around."; + LayoutData.DataChanged(); + + InitializeDiagram(); + } + + private void InitializeDiagram() + { + var node1 = NewNode(50, 80); + var node2 = NewNode(200, 350); + var node3 = NewNode(400, 100); + + _diagram.Nodes.Add(new[] { node1, node2, node3 }); + + var link1 = new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) + { + PathGenerator = PathGenerators.Straight, + Segmentable = true + }; + link1.Labels.Add(new LinkLabelModel(link1, "Content")); + link1.Vertices.Add(new LinkVertexModel(link1, new Point(221, 117))); + link1.Vertices.Add(new LinkVertexModel(link1, new Point(111, 291))); + + var link2 = new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Left)) + { + PathGenerator = PathGenerators.Smooth, // default + Segmentable = true + }; + link2.Labels.Add(new LinkLabelModel(link2, "Content")); + link2.Vertices.Add(new LinkVertexModel(link2, new Point(400, 324))); + link2.Vertices.Add(new LinkVertexModel(link2, new Point(326, 180))); + + _diagram.Links.Add(new[] { link1, link2 }); + } + + private NodeModel NewNode(double x, double y) + { + var node = new NodeModel(new Point(x, y)); + node.AddPort(PortAlignment.Bottom); + node.AddPort(PortAlignment.Top); + node.AddPort(PortAlignment.Left); + node.AddPort(PortAlignment.Right); + return node; + } + } +} diff --git a/samples/SharedDemo/Demos/Simple.razor.cs b/samples/SharedDemo/Demos/Simple.razor.cs index 6f9f5966..c725c00b 100644 --- a/samples/SharedDemo/Demos/Simple.razor.cs +++ b/samples/SharedDemo/Demos/Simple.razor.cs @@ -15,8 +15,21 @@ protected override void OnInitialized() var node1 = NewNode(50, 50); var node2 = NewNode(300, 300); - diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left))); - diagram.Nodes.Add(new[] { node1, node2, NewNode(300, 50) }); + var node3 = NewNode(300, 50); + diagram.Nodes.Add(new[] { node1, node2, node3 }); + + diagram.Links.Add(new LinkModel(node1.GetPort(PortAlignment.Right), node2.GetPort(PortAlignment.Left)) + { + SourceMarker = LinkMarker.Arrow, + TargetMarker = LinkMarker.Arrow + }); + diagram.Links.Add(new LinkModel(node2.GetPort(PortAlignment.Right), node3.GetPort(PortAlignment.Right)) + { + Router = Routers.Orthogonal, + PathGenerator = PathGenerators.Straight, + SourceMarker = LinkMarker.Arrow, + TargetMarker = LinkMarker.Arrow + }); } protected void ToggleZoom() => diagram.Options.Zoom.Enabled = !diagram.Options.Zoom.Enabled; diff --git a/samples/SharedDemo/Layouts/DemoLayout.razor b/samples/SharedDemo/Layouts/DemoLayout.razor index 0c56ed2e..147066d4 100644 --- a/samples/SharedDemo/Layouts/DemoLayout.razor +++ b/samples/SharedDemo/Layouts/DemoLayout.razor @@ -30,26 +30,53 @@