From 60f8ee19f8ced4a8f79467c09c3713725600d54f Mon Sep 17 00:00:00 2001
From: Haytam Zanid
diff --git a/site/Site/Pages/Documentation/Groups/Customization.razor b/site/Site/Pages/Documentation/Groups/Customization.razor
new file mode 100644
index 000000000..aaba04be2
--- /dev/null
+++ b/site/Site/Pages/Documentation/Groups/Customization.razor
@@ -0,0 +1,12 @@
+@page "/documentation/groups-customization"
+@layout DocumentationLayout
+@inherits DocumentationPage
+
+
+ In Blazor Diagrams, Groups are a way to group nodes together.
+ Customizing nodes in Blazor Diagrams is very easy!
+
+ Most of the time, your nodes will hold more information than just a title, which is why we create a new model to encapsulate all the data and behavior we want.
+ In a perfect world, you would have one node model for each behavior and or UI representation.
+ Let's create a UI component to control how the node looks like:
+
+ All we have to do now is register our new creation!
+
+ Nodes are the most important concept in Blazor Diagrams.
+
+ The component
+ The classes that the div can have (beside
+ SVG nodes are nodes that will be rendered in the SVG layer.
+ The component Add
+
+
+
+ @foreach (var port in Node.Ports)
+ {
+ // In case you have any ports to show
+ // IMPORTANT: You are always in charge of rendering ports
+ Class
parameter.
<svg class="diagram-svg-layer" style="transform: translate(0px, 0px) scale(1); z-index: 0;">
+ <!-- Links, Nodes, Controls, ... -->
+</svg>
Groups Customization
+
+
+ Groups can also contain other groups, so you can create hierarchies.
+Interactively
-First, we will need how groups are created:
+First, we will need to change how groups are created:
-
yourDiagram.Options.Groups.Factory = (diagram, children) => new SvgGroupModel(children);
@@ -56,5 +56,5 @@ var node2 = new SvgNodeModel(new Point(50, 50));
var group = new SvgGroupModel(new[] { node1, node2 });
Nodes Customization
+
+Creating a model
+
+
+
+ Let's assume that we want to create a new node that represents addition:
+
+
+
+using Blazor.Diagrams.Core.Geometry;
+using Blazor.Diagrams.Core.Models;
+
+namespace YourNamespace;
+
+public class AddTwoNumbersNode : NodeModel
+{
+ public AddTwoNumbersNode(Point? position = null) : base(position) { }
+
+ public double FirstNumber { get; set; }
+ public double SecondNumber { get; set; }
+
+ // Here, you can put whatever you want, such as a method that does the addition
+}
+
Creating a component
+
+
+
+Let's also style our component!
+
+
+@@using Blazor.Diagrams.Components.Renderers;
+@@using Site.Models.Nodes;
+
+<div>
+ <h5 class="card-title">Add</h5>
+ <input type="number" class="form-control" @@bind-value="Node.FirstNumber" placeholder="Number 1" />
+ <input type="number" class="form-control" @@bind-value="Node.SecondNumber" placeholder="Number 2" />
+
+ @@foreach (var port in Node.Ports)
+ {
+ // In case you have any ports to show
+ // IMPORTANT: You are always in charge of rendering ports
+ <PortRenderer @@key="port" Port="port" />
+ }
+</div>
+
+@@code {
+ // This gets filled by the library
+ [Parameter] public AddTwoNumbersNode Node { get; set; } = null!;
+}
+
+
+
+div {
+ width: 230px;
+ outline: 1px solid black;
+ padding: 20px;
+ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+}
+
+ div > h5 {
+ font-weight: 600;
+ text-transform: uppercase;
+ margin-bottom: 10px;
+ }
+
+ div > input[type=number] {
+ padding: 3px;
+ border-radius: 3px;
+ border: 1px solid black;
+ margin-bottom: 8px;
+ }
+
+.diagram-port {
+ position: absolute;
+ width: 30px;
+ height: 20px;
+ background-color: black;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+ .diagram-port.top {
+ border-top-left-radius: 50%;
+ border-top-right-radius: 50%;
+ top: -10px;
+ }
+
+ .diagram-port.bottom {
+ border-bottom-left-radius: 50%;
+ border-bottom-right-radius: 50%;
+ bottom: 10px;
+ }
+
Displaying
+
+
+
+
+private BlazorDiagram Diagram { get; set; } = new();
+
+protected override void OnInitialized()
+{
+ base.OnInitialized();
+
+ Diagram.RegisterComponent<AddTwoNumbersNode, AddTwoNumbersWidget>();
+
+ var node = Diagram.Nodes.Add(new AddTwoNumbersNode(new Point(80, 80)));
+ node.AddPort(PortAlignment.Top);
+ node.AddPort(PortAlignment.Bottom);
+}
+
Nodes
+
+Structure
+
+NodeRenderer
generates the follolwing structure:
+
+
+
+<div class="diagram-node ..."
+ data-node-id="28e9606d-08dd-47d5-a4c7-b25e541bcf1e"
+ style="top: 10px; left: 10px;">
+ <!-- YOUR CONTENT WILL BE HERE -->
+</div>
+
diagram-node
) are: locked
, selected
and grouped
.
+Creating a node
+
+
+
+
+var node1 = new NodeModel(new Point(10, 10));
+BlazorDiagram.Nodes.Add(node1);
+
SVG Nodes
+
+
+ In this page, we will talk about the differences between them and normal nodes.
+Structure
+
+NodeRenderer
generates the follolwing structure:
+
+
+
+<g class="diagram-node ..."
+ data-node-id="28e9606d-08dd-47d5-a4c7-b25e541bcf1e"
+ transform="translate(10 10)">
+ <!-- YOUR CONTENT WILL BE HERE -->
+</g>
+
Creating a SVG node
+
+
+
+
+var node1 = new SvgNodeModel(new Point(10, 10));
+BlazorDiagram.Nodes.Add(node1);
+
ShapeAnglePositionProvider
: To return a position for the given angle.NavigatorWidget
: To know what to draw.
+ You can change the shape of your node by overriding the GetShape
method in your custom model.
+
diff --git a/site/Site/Pages/Documentation/Nodes/SvgCustomization.razor b/site/Site/Pages/Documentation/Nodes/SvgCustomization.razor
new file mode 100644
index 000000000..57961438d
--- /dev/null
+++ b/site/Site/Pages/Documentation/Nodes/SvgCustomization.razor
@@ -0,0 +1,158 @@
+@page "/documentation/nodes-customization-svg"
+@using Site.Components.Documentation.Nodes;
+@using Site.Models.Nodes;
+@layout DocumentationLayout
+@inherits DocumentationPage
+
+SVG Nodes Customization - Documentation - Blazor Diagrams
+
+SVG Nodes Customization
+
+
+ Creating a custom SVG node is as easy as a HTML one, let's go!
+
+
+Creating a model
+
+
+ Let's assume that we want to create a new node that represents Gingerbread:
+
+
+GingerbreadNode.cs
+
+using Blazor.Diagrams.Core.Geometry;
+using Blazor.Diagrams.Core.Models;
+
+namespace YourNamespace;
+
+public class AddTwoNumbersNode : NodeModel
+{
+ public AddTwoNumbersNode(Point? position = null) : base(position) { }
+
+ public double FirstNumber { get; set; }
+ public double SecondNumber { get; set; }
+
+ // Here, you can put whatever you want, such as a method that does the addition
+}
+
+
+Creating a component
+
+
+ Let's create a UI component to control how the node looks like:
+
+
+AddTwoNumbersWidget.razor
+
+@@using Blazor.Diagrams.Components.Renderers;
+@@using Site.Models.Nodes;
+
+<div>
+ <h5 class="card-title">Add</h5>
+ <input type="number" class="form-control" @@bind-value="Node.FirstNumber" placeholder="Number 1" />
+ <input type="number" class="form-control" @@bind-value="Node.SecondNumber" placeholder="Number 2" />
+
+ @@foreach (var port in Node.Ports)
+ {
+ // In case you have any ports to show
+ // IMPORTANT: You are always in charge of rendering ports
+ <PortRenderer @@key="port" Port="port" />
+ }
+</div>
+
+@@code {
+ // This gets filled by the library
+ [Parameter] public AddTwoNumbersNode Node { get; set; } = null!;
+}
+
+
+Let's also style our component!
+
+AddTwoNumbersWidget.razor.css
+
+div {
+ width: 230px;
+ outline: 1px solid black;
+ padding: 20px;
+ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+}
+
+ div > h5 {
+ font-weight: 600;
+ text-transform: uppercase;
+ margin-bottom: 10px;
+ }
+
+ div > input[type=number] {
+ padding: 3px;
+ border-radius: 3px;
+ border: 1px solid black;
+ margin-bottom: 8px;
+ }
+
+::deep .diagram-port {
+ position: absolute;
+ width: 30px;
+ height: 20px;
+ background-color: black;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+ ::deep .diagram-port.top {
+ border-top-left-radius: 50%;
+ border-top-right-radius: 50%;
+ top: -10px;
+ }
+
+ ::deep .diagram-port.bottom {
+ border-bottom-left-radius: 50%;
+ border-bottom-right-radius: 50%;
+ bottom: -30px;
+ }
+
+
+Displaying
+
+
+ All we have to do now is register our new creation!
+
+
+
+private BlazorDiagram Diagram { get; set; } = new();
+
+protected override void OnInitialized()
+{
+ base.OnInitialized();
+
+ Diagram.RegisterComponent<AddTwoNumbersNode, AddTwoNumbersWidget>();
+
+ var node = Diagram.Nodes.Add(new AddTwoNumbersNode(new Point(80, 80)));
+ node.AddPort(PortAlignment.Top);
+ node.AddPort(PortAlignment.Bottom);
+}
+
+
+
+
+
+
+
+
+
+
+@code {
+ private BlazorDiagram Diagram { get; set; } = new();
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ Diagram.RegisterComponent();
+
+ var node = Diagram.Nodes.Add(new GingerbreadNode(new Point(80, 80)));
+ node.AddPort(PortAlignment.Top);
+ node.AddPort(PortAlignment.Bottom);
+ }
+}
\ No newline at end of file
diff --git a/site/Site/Pages/Documentation/Nodes/SvgCustomization.razor.css b/site/Site/Pages/Documentation/Nodes/SvgCustomization.razor.css
new file mode 100644
index 000000000..6f4f39fa9
--- /dev/null
+++ b/site/Site/Pages/Documentation/Nodes/SvgCustomization.razor.css
@@ -0,0 +1,6 @@
+.diagram-container {
+ width: 100%;
+ height: 400px;
+ border: 1px solid black;
+ user-select: none;
+}
diff --git a/site/Site/Shared/DocumentationLayout.razor.cs b/site/Site/Shared/DocumentationLayout.razor.cs
index 0f0984fb2..e5d0a0415 100644
--- a/site/Site/Shared/DocumentationLayout.razor.cs
+++ b/site/Site/Shared/DocumentationLayout.razor.cs
@@ -1,31 +1,30 @@
using Microsoft.AspNetCore.Components;
-namespace Site.Shared
+namespace Site.Shared;
+
+public partial class DocumentationLayout
{
- public partial class DocumentationLayout
- {
- [Inject] private NavigationManager NavigationManager { get; set; } = null!;
+ [Inject] private NavigationManager NavigationManager { get; set; } = null!;
- private (string, string) GetMenuItemExtraClasses(string link)
- {
- if (IsActive(link, false))
- return ("font-semibold text-main", "bg-main text-white");
+ private (string, string) GetMenuItemExtraClasses(string link)
+ {
+ if (IsActive(link, false))
+ return ("font-semibold text-main", "bg-main text-white");
- return ("font-medium hover:text-slate-900", "bg-gray-100 text-black");
- }
+ return ("font-medium hover:text-slate-900", "bg-gray-100 text-black");
+ }
- private string GetGroupMenuItemExtraClasses(string link)
- {
- if (IsActive(link, true))
- return "text-palette-main border-palette-main font-semibold";
+ private string GetGroupMenuItemExtraClasses(string link)
+ {
+ if (IsActive(link, true))
+ return "text-palette-main border-palette-main font-semibold";
- return "text-slate-700 hover:border-slate-400 hover:text-slate-900";
- }
+ return "text-slate-700 hover:border-slate-400 hover:text-slate-900";
+ }
- private bool IsActive(string link, bool fullMatch)
- {
- var relativePath = "/" + NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLower();
- return (fullMatch && relativePath == link) || (!fullMatch && relativePath.StartsWith(link));
- }
+ private bool IsActive(string link, bool fullMatch)
+ {
+ var relativePath = "/" + NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLower();
+ return (fullMatch && relativePath == link) || (!fullMatch && relativePath.StartsWith(link));
}
}
diff --git a/site/Site/Static/Documentation.cs b/site/Site/Static/Documentation.cs
index 0fd8a9c4f..3ad2fb3b1 100644
--- a/site/Site/Static/Documentation.cs
+++ b/site/Site/Static/Documentation.cs
@@ -1,40 +1,40 @@
using Site.Models.Documentation;
-namespace Site.Static
+namespace Site.Static;
+
+public static class Documentation
{
- public static class Documentation
+ public static readonly Menu Menu = new(new List
+ {
+ new MenuItem("Documentation", "/documentation", Icons.BookOpen),
+ //new MenuItem("Examples", "/examples", Icons.FolderOpen),
+ }, new List
{
- public static readonly Menu Menu = new(new List
+ new MenuGroup("Getting Started", new List
+ {
+ new MenuItem("Installation", "/documentation/installation"),
+ new MenuItem("Diagram Creation", "/documentation/diagram-creation"),
+ new MenuItem("Display", "/documentation/display"),
+ }),
+ new MenuGroup("Diagram", new List
+ {
+ new MenuItem("Overview", "/documentation/diagram"),
+ new MenuItem("Behaviors", "/documentation/diagram-behaviors"),
+ new MenuItem("Options", "/documentation/diagram-options"),
+ new MenuItem("Keyboard Shortcuts", "/documentation/keyboard-shortcuts"),
+ new MenuItem("API", "/documentation/diagram-api"),
+ }),
+ new MenuGroup("Nodes", new List
{
- new MenuItem("Documentation", "/documentation", Icons.BookOpen),
- new MenuItem("Examples", "/examples", Icons.FolderOpen),
- }, new List
+ new MenuItem("Overview", "/documentation/nodes"),
+ new MenuItem("SVG", "/documentation/nodes-svg"),
+ new MenuItem("Customization", "/documentation/nodes-customization"),
+ new MenuItem("Customization (SVG)", "/documentation/nodes-customization-svg")
+ }),
+ new MenuGroup("Groups", new List
{
- new MenuGroup("Getting Started", new List
- {
- new MenuItem("Installation", "/documentation/installation"),
- new MenuItem("Diagram Creation", "/documentation/diagram-creation"),
- new MenuItem("Display", "/documentation/display"),
- }),
- new MenuGroup("Diagram", new List
- {
- new MenuItem("Overview", "/documentation/diagram"),
- new MenuItem("Behaviors", "/documentation/diagram-behaviors"),
- new MenuItem("Options", "/documentation/diagram-options"),
- new MenuItem("Keyboard Shortcuts", "/documentation/keyboard-shortcuts"),
- new MenuItem("API", "/documentation/diagram-api"),
- }),
- new MenuGroup("Nodes", new List
- {
- new MenuItem("Overview", "/documentation/nodes"),
- new MenuItem("SVG", "/documentation/nodes-svg"),
- new MenuItem("Customization", "/documentation/nodes-customization")
- }),
- new MenuGroup("Groups", new List
- {
- new MenuItem("Overview", "/documentation/groups"),
- new MenuItem("SVG", "/documentation/groups-svg"),
- })
- });
- }
+ new MenuItem("Overview", "/documentation/groups"),
+ new MenuItem("SVG", "/documentation/groups-svg"),
+ })
+ });
}
diff --git a/site/Site/Static/Icons.cs b/site/Site/Static/Icons.cs
index 078529be9..2e0df5f81 100644
--- a/site/Site/Static/Icons.cs
+++ b/site/Site/Static/Icons.cs
@@ -1,9 +1,8 @@
-namespace Site.Static
+namespace Site.Static;
+
+public static class Icons
{
- public static class Icons
- {
- public static readonly string Github = "M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z";
- public static readonly string BookOpen = "M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25";
- public static readonly string FolderOpen = "M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776";
- }
+ public static readonly string Github = "M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z";
+ public static readonly string BookOpen = "M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25";
+ public static readonly string FolderOpen = "M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776";
}
diff --git a/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs b/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs
index 8cda7518d..3c7d6d816 100644
--- a/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs
+++ b/src/Blazor.Diagrams.Algorithms/LinksReconnectionAlgorithms.cs
@@ -4,63 +4,62 @@
using System.Linq;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Algorithms
+namespace Blazor.Diagrams.Algorithms;
+
+public static class LinksReconnectionAlgorithms
{
- public static class LinksReconnectionAlgorithms
+ public static void ReconnectLinksToClosestPorts(this Diagram diagram)
{
- public static void ReconnectLinksToClosestPorts(this Diagram diagram)
- {
- // Only refresh ports once
- var modelsToRefresh = new HashSet();
+ // Only refresh ports once
+ var modelsToRefresh = new HashSet();
- foreach (var link in diagram.Links.ToArray())
- {
- if (link.Source is not SinglePortAnchor spa1 || link.Target is not SinglePortAnchor spa2)
- continue;
+ foreach (var link in diagram.Links.ToArray())
+ {
+ if (link.Source is not SinglePortAnchor spa1 || link.Target is not SinglePortAnchor spa2)
+ continue;
- var sourcePorts = spa1.Port.Parent.Ports;
- var targetPorts = spa2.Port.Parent.Ports;
+ var sourcePorts = spa1.Port.Parent.Ports;
+ var targetPorts = spa2.Port.Parent.Ports;
- // Find the ports with minimal distance
- var minDistance = double.MaxValue;
- var minSourcePort = spa1.Port;
- var minTargetPort = spa2.Port;
- foreach (var sourcePort in sourcePorts)
+ // Find the ports with minimal distance
+ var minDistance = double.MaxValue;
+ var minSourcePort = spa1.Port;
+ var minTargetPort = spa2.Port;
+ foreach (var sourcePort in sourcePorts)
+ {
+ foreach (var targetPort in targetPorts)
{
- foreach (var targetPort in targetPorts)
+ var distance = sourcePort.Position.DistanceTo(targetPort.Position);
+ if (distance < minDistance)
{
- var distance = sourcePort.Position.DistanceTo(targetPort.Position);
- if (distance < minDistance)
- {
- minDistance = distance;
- minSourcePort = sourcePort;
- minTargetPort = targetPort;
- }
+ minDistance = distance;
+ minSourcePort = sourcePort;
+ minTargetPort = targetPort;
}
}
+ }
- // Reconnect
- if (spa1.Port != minSourcePort)
- {
- modelsToRefresh.Add(spa1.Port);
- modelsToRefresh.Add(minSourcePort);
- link.SetSource(new SinglePortAnchor(minSourcePort));
- modelsToRefresh.Add(link);
- }
-
- if (spa2.Port != minTargetPort)
- {
- modelsToRefresh.Add(spa2.Port);
- modelsToRefresh.Add(minTargetPort);
- link.SetTarget(new SinglePortAnchor(minTargetPort));
- modelsToRefresh.Add(link);
- }
+ // Reconnect
+ if (spa1.Port != minSourcePort)
+ {
+ modelsToRefresh.Add(spa1.Port);
+ modelsToRefresh.Add(minSourcePort);
+ link.SetSource(new SinglePortAnchor(minSourcePort));
+ modelsToRefresh.Add(link);
}
- foreach (var model in modelsToRefresh)
+ if (spa2.Port != minTargetPort)
{
- model.Refresh();
+ modelsToRefresh.Add(spa2.Port);
+ modelsToRefresh.Add(minTargetPort);
+ link.SetTarget(new SinglePortAnchor(minTargetPort));
+ modelsToRefresh.Add(link);
}
}
+
+ foreach (var model in modelsToRefresh)
+ {
+ model.Refresh();
+ }
}
}
diff --git a/src/Blazor.Diagrams.Core/Anchors/Anchor.cs b/src/Blazor.Diagrams.Core/Anchors/Anchor.cs
index 78602d83a..53f100fdf 100644
--- a/src/Blazor.Diagrams.Core/Anchors/Anchor.cs
+++ b/src/Blazor.Diagrams.Core/Anchors/Anchor.cs
@@ -3,48 +3,47 @@
using System;
using System.Collections.Generic;
-namespace Blazor.Diagrams.Core.Anchors
+namespace Blazor.Diagrams.Core.Anchors;
+
+public abstract class Anchor
{
- public abstract class Anchor
+ public Anchor(ILinkable? model = null)
{
- public Anchor(ILinkable? model = null)
- {
- Model = model;
- }
+ Model = model;
+ }
- public ILinkable? Model { get; }
+ public ILinkable? Model { get; }
- public abstract Point? GetPosition(BaseLinkModel link, Point[] route);
+ public abstract Point? GetPosition(BaseLinkModel link, Point[] route);
- public abstract Point? GetPlainPosition();
+ public abstract Point? GetPlainPosition();
- public Point? GetPosition(BaseLinkModel link) => GetPosition(link, Array.Empty());
+ public Point? GetPosition(BaseLinkModel link) => GetPosition(link, Array.Empty());
- protected static Point? GetOtherPosition(BaseLinkModel link, bool isTarget)
- {
- var anchor = isTarget ? link.Source : link.Target!;
- return anchor.GetPlainPosition();
- }
+ protected static Point? GetOtherPosition(BaseLinkModel link, bool isTarget)
+ {
+ var anchor = isTarget ? link.Source : link.Target!;
+ return anchor.GetPlainPosition();
+ }
- protected static Point? GetClosestPointTo(IEnumerable points, Point point)
- {
- var minDist = double.MaxValue;
- Point? minPoint = null;
+ protected static Point? GetClosestPointTo(IEnumerable points, Point point)
+ {
+ var minDist = double.MaxValue;
+ Point? minPoint = null;
- foreach (var pt in points)
+ foreach (var pt in points)
+ {
+ if (pt == null)
+ continue;
+
+ var dist = pt.DistanceTo(point);
+ if (dist < minDist)
{
- if (pt == null)
- continue;
-
- var dist = pt.DistanceTo(point);
- if (dist < minDist)
- {
- minDist = dist;
- minPoint = pt;
- }
+ minDist = dist;
+ minPoint = pt;
}
-
- return minPoint;
}
+
+ return minPoint;
}
}
diff --git a/src/Blazor.Diagrams.Core/Anchors/DynamicAnchor.cs b/src/Blazor.Diagrams.Core/Anchors/DynamicAnchor.cs
index ef1f8a54c..7f203c46d 100644
--- a/src/Blazor.Diagrams.Core/Anchors/DynamicAnchor.cs
+++ b/src/Blazor.Diagrams.Core/Anchors/DynamicAnchor.cs
@@ -5,33 +5,32 @@
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Core.Positions;
-namespace Blazor.Diagrams.Core.Anchors
+namespace Blazor.Diagrams.Core.Anchors;
+
+public sealed class DynamicAnchor : Anchor
{
- public sealed class DynamicAnchor : Anchor
+ public DynamicAnchor(NodeModel model, IPositionProvider[] providers) : base(model)
{
- public DynamicAnchor(NodeModel model, IPositionProvider[] providers) : base(model)
- {
- if (providers.Length == 0)
- throw new InvalidOperationException("No providers provided");
-
- Node = model;
- Providers = providers;
- }
+ if (providers.Length == 0)
+ throw new InvalidOperationException("No providers provided");
- public NodeModel Node { get; }
- public IPositionProvider[] Providers { get; }
+ Node = model;
+ Providers = providers;
+ }
- public override Point? GetPosition(BaseLinkModel link, Point[] route)
- {
- if (Node.Size == null)
- return null;
+ public NodeModel Node { get; }
+ public IPositionProvider[] Providers { get; }
- var isTarget = link.Target == this;
- var pt = route.Length > 0 ? route[isTarget ? ^1 : 0] : GetOtherPosition(link, isTarget);
- var positions = Providers.Select(e => e.GetPosition(Node));
- return pt is null ? null : GetClosestPointTo(positions, pt);
- }
+ public override Point? GetPosition(BaseLinkModel link, Point[] route)
+ {
+ if (Node.Size == null)
+ return null;
- public override Point? GetPlainPosition() => Node.GetBounds()?.Center ?? null;
+ var isTarget = link.Target == this;
+ var pt = route.Length > 0 ? route[isTarget ? ^1 : 0] : GetOtherPosition(link, isTarget);
+ var positions = Providers.Select(e => e.GetPosition(Node));
+ return pt is null ? null : GetClosestPointTo(positions, pt);
}
+
+ public override Point? GetPlainPosition() => Node.GetBounds()?.Center ?? null;
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams.Core/Anchors/PositionAnchor.cs b/src/Blazor.Diagrams.Core/Anchors/PositionAnchor.cs
index 09113bee4..1b9823f82 100644
--- a/src/Blazor.Diagrams.Core/Anchors/PositionAnchor.cs
+++ b/src/Blazor.Diagrams.Core/Anchors/PositionAnchor.cs
@@ -1,21 +1,20 @@
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Anchors
+namespace Blazor.Diagrams.Core.Anchors;
+
+public sealed class PositionAnchor : Anchor
{
- public sealed class PositionAnchor : Anchor
- {
- private Point _position;
+ private Point _position;
- public PositionAnchor(Point position) : base(null)
- {
- _position = position;
- }
+ public PositionAnchor(Point position) : base(null)
+ {
+ _position = position;
+ }
- public void SetPosition(Point position) => _position = position;
+ public void SetPosition(Point position) => _position = position;
- public override Point? GetPlainPosition() => _position;
+ public override Point? GetPlainPosition() => _position;
- public override Point? GetPosition(BaseLinkModel link, Point[] route) => _position;
- }
+ public override Point? GetPosition(BaseLinkModel link, Point[] route) => _position;
}
diff --git a/src/Blazor.Diagrams.Core/Anchors/ShapeIntersectionAnchor.cs b/src/Blazor.Diagrams.Core/Anchors/ShapeIntersectionAnchor.cs
index bdd17bbfd..8790e401f 100644
--- a/src/Blazor.Diagrams.Core/Anchors/ShapeIntersectionAnchor.cs
+++ b/src/Blazor.Diagrams.Core/Anchors/ShapeIntersectionAnchor.cs
@@ -2,41 +2,40 @@
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Anchors
+namespace Blazor.Diagrams.Core.Anchors;
+
+public sealed class ShapeIntersectionAnchor : Anchor
{
- public sealed class ShapeIntersectionAnchor : Anchor
+ public ShapeIntersectionAnchor(NodeModel model) : base(model)
{
- public ShapeIntersectionAnchor(NodeModel model) : base(model)
- {
- Node = model;
- }
+ Node = model;
+ }
+
+ public NodeModel Node { get; }
- public NodeModel Node { get; }
+ public override Point? GetPosition(BaseLinkModel link, Point[] route)
+ {
+ if (Node.Size == null)
+ return null;
- public override Point? GetPosition(BaseLinkModel link, Point[] route)
+ var isTarget = link.Target == this;
+ var nodeCenter = Node.GetBounds()!.Center;
+ Point? pt;
+ if (route.Length > 0)
+ {
+ pt = route[isTarget ? ^1 : 0];
+ }
+ else
{
- if (Node.Size == null)
- return null;
-
- var isTarget = link.Target == this;
- var nodeCenter = Node.GetBounds()!.Center;
- Point? pt;
- if (route.Length > 0)
- {
- pt = route[isTarget ? ^1 : 0];
- }
- else
- {
- pt = GetOtherPosition(link, isTarget);
- }
-
- if (pt is null) return null;
-
- var line = new Line(pt, nodeCenter);
- var intersections = Node.GetShape().GetIntersectionsWithLine(line);
- return GetClosestPointTo(intersections, pt); // Todo: use Offset
+ pt = GetOtherPosition(link, isTarget);
}
- public override Point? GetPlainPosition() => Node.GetBounds()?.Center ?? null;
+ if (pt is null) return null;
+
+ var line = new Line(pt, nodeCenter);
+ var intersections = Node.GetShape().GetIntersectionsWithLine(line);
+ return GetClosestPointTo(intersections, pt); // Todo: use Offset
}
+
+ public override Point? GetPlainPosition() => Node.GetBounds()?.Center ?? null;
}
diff --git a/src/Blazor.Diagrams.Core/Anchors/SinglePortAnchor.cs b/src/Blazor.Diagrams.Core/Anchors/SinglePortAnchor.cs
index ca2cc2eaa..6cd1f8de2 100644
--- a/src/Blazor.Diagrams.Core/Anchors/SinglePortAnchor.cs
+++ b/src/Blazor.Diagrams.Core/Anchors/SinglePortAnchor.cs
@@ -2,57 +2,56 @@
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Anchors
+namespace Blazor.Diagrams.Core.Anchors;
+
+public sealed class SinglePortAnchor : Anchor
{
- public sealed class SinglePortAnchor : Anchor
+ public SinglePortAnchor(PortModel port) : base(port)
{
- public SinglePortAnchor(PortModel port) : base(port)
- {
- Port = port;
- }
-
- public PortModel Port { get; }
- public bool MiddleIfNoMarker { get; set; } = false;
- public bool UseShapeAndAlignment { get; set; } = true;
-
- public override Point? GetPosition(BaseLinkModel link, Point[] route)
- {
- if (!Port.Initialized)
- return null;
+ Port = port;
+ }
+
+ public PortModel Port { get; }
+ public bool MiddleIfNoMarker { get; set; } = false;
+ public bool UseShapeAndAlignment { get; set; } = true;
- if (MiddleIfNoMarker && ((link.Source == this && link.SourceMarker is null) || (link.Target == this && link.TargetMarker is null)))
- return Port.MiddlePosition;
-
- var pt = Port.Position;
- if (UseShapeAndAlignment)
- {
- return Port.Alignment switch
- {
- PortAlignment.Top => Port.GetShape().GetPointAtAngle(270),
- PortAlignment.TopRight => Port.GetShape().GetPointAtAngle(315),
- PortAlignment.Right => Port.GetShape().GetPointAtAngle(0),
- PortAlignment.BottomRight => Port.GetShape().GetPointAtAngle(45),
- PortAlignment.Bottom => Port.GetShape().GetPointAtAngle(90),
- PortAlignment.BottomLeft => Port.GetShape().GetPointAtAngle(135),
- PortAlignment.Left => Port.GetShape().GetPointAtAngle(180),
- PortAlignment.TopLeft => Port.GetShape().GetPointAtAngle(225),
- _ => null,
- };
- }
+ public override Point? GetPosition(BaseLinkModel link, Point[] route)
+ {
+ if (!Port.Initialized)
+ return null;
+ if (MiddleIfNoMarker && ((link.Source == this && link.SourceMarker is null) || (link.Target == this && link.TargetMarker is null)))
+ return Port.MiddlePosition;
+
+ var pt = Port.Position;
+ if (UseShapeAndAlignment)
+ {
return Port.Alignment switch
{
- PortAlignment.Top => new Point(pt.X + Port.Size.Width / 2, pt.Y),
- PortAlignment.TopRight => new Point(pt.X + Port.Size.Width, pt.Y),
- PortAlignment.Right => new Point(pt.X + Port.Size.Width, pt.Y + Port.Size.Height / 2),
- PortAlignment.BottomRight => new Point(pt.X + Port.Size.Width, pt.Y + Port.Size.Height),
- PortAlignment.Bottom => new Point(pt.X + Port.Size.Width / 2, pt.Y + Port.Size.Height),
- PortAlignment.BottomLeft => new Point(pt.X, pt.Y + Port.Size.Height),
- PortAlignment.Left => new Point(pt.X, pt.Y + Port.Size.Height / 2),
- _ => pt,
+ PortAlignment.Top => Port.GetShape().GetPointAtAngle(270),
+ PortAlignment.TopRight => Port.GetShape().GetPointAtAngle(315),
+ PortAlignment.Right => Port.GetShape().GetPointAtAngle(0),
+ PortAlignment.BottomRight => Port.GetShape().GetPointAtAngle(45),
+ PortAlignment.Bottom => Port.GetShape().GetPointAtAngle(90),
+ PortAlignment.BottomLeft => Port.GetShape().GetPointAtAngle(135),
+ PortAlignment.Left => Port.GetShape().GetPointAtAngle(180),
+ PortAlignment.TopLeft => Port.GetShape().GetPointAtAngle(225),
+ _ => null,
};
}
- public override Point? GetPlainPosition() => Port.MiddlePosition;
+ return Port.Alignment switch
+ {
+ PortAlignment.Top => new Point(pt.X + Port.Size.Width / 2, pt.Y),
+ PortAlignment.TopRight => new Point(pt.X + Port.Size.Width, pt.Y),
+ PortAlignment.Right => new Point(pt.X + Port.Size.Width, pt.Y + Port.Size.Height / 2),
+ PortAlignment.BottomRight => new Point(pt.X + Port.Size.Width, pt.Y + Port.Size.Height),
+ PortAlignment.Bottom => new Point(pt.X + Port.Size.Width / 2, pt.Y + Port.Size.Height),
+ PortAlignment.BottomLeft => new Point(pt.X, pt.Y + Port.Size.Height),
+ PortAlignment.Left => new Point(pt.X, pt.Y + Port.Size.Height / 2),
+ _ => pt,
+ };
}
+
+ public override Point? GetPlainPosition() => Port.MiddlePosition;
}
diff --git a/src/Blazor.Diagrams.Core/Behavior.cs b/src/Blazor.Diagrams.Core/Behavior.cs
index 2d81088f5..78e020602 100644
--- a/src/Blazor.Diagrams.Core/Behavior.cs
+++ b/src/Blazor.Diagrams.Core/Behavior.cs
@@ -1,16 +1,15 @@
using System;
-namespace Blazor.Diagrams.Core
+namespace Blazor.Diagrams.Core;
+
+public abstract class Behavior : IDisposable
{
- public abstract class Behavior : IDisposable
+ public Behavior(Diagram diagram)
{
- public Behavior(Diagram diagram)
- {
- Diagram = diagram;
- }
+ Diagram = diagram;
+ }
- protected Diagram Diagram { get; }
+ protected Diagram Diagram { get; }
- public abstract void Dispose();
- }
+ public abstract void Dispose();
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs
index ff15e342c..f3fe081d1 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/DebugEventsBehavior.cs
@@ -2,93 +2,92 @@
using Blazor.Diagrams.Core.Models.Base;
using System;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class DebugEventsBehavior : Behavior
{
- public class DebugEventsBehavior : Behavior
+ public DebugEventsBehavior(Diagram diagram) : base(diagram)
{
- public DebugEventsBehavior(Diagram diagram) : base(diagram)
- {
- 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.Groups.Added += Diagram_GroupAdded;
- Diagram.Groups.Removed += Diagram_GroupRemoved;
- Diagram.SelectionChanged += Diagram_SelectionChanged;
- Diagram.ZoomChanged += Diagram_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.Groups.Added += Diagram_GroupAdded;
+ Diagram.Groups.Removed += Diagram_GroupRemoved;
+ Diagram.SelectionChanged += Diagram_SelectionChanged;
+ Diagram.ZoomChanged += Diagram_ZoomChanged;
+ }
- private void Diagram_ZoomChanged()
- {
- Console.WriteLine($"ZoomChanged, Zoom={Diagram.Zoom}");
- }
+ private void Diagram_ZoomChanged()
+ {
+ Console.WriteLine($"ZoomChanged, Zoom={Diagram.Zoom}");
+ }
- private void Diagram_SelectionChanged(SelectableModel obj)
- {
- Console.WriteLine($"SelectionChanged, Model={obj.GetType().Name}, Selected={obj.Selected}");
- }
+ private void Diagram_SelectionChanged(SelectableModel obj)
+ {
+ Console.WriteLine($"SelectionChanged, Model={obj.GetType().Name}, Selected={obj.Selected}");
+ }
- private void Links_Removed(BaseLinkModel obj)
- {
- Console.WriteLine($"Links.Removed, Links=[{obj}]");
- }
+ private void Links_Removed(BaseLinkModel obj)
+ {
+ Console.WriteLine($"Links.Removed, Links=[{obj}]");
+ }
- private void Links_Added(BaseLinkModel obj)
- {
- Console.WriteLine($"Links.Added, Links=[{obj}]");
- }
+ private void Links_Added(BaseLinkModel obj)
+ {
+ Console.WriteLine($"Links.Added, Links=[{obj}]");
+ }
- private void Nodes_Removed(NodeModel obj)
- {
- Console.WriteLine($"Nodes.Removed, Nodes=[{obj}]");
- }
+ private void Nodes_Removed(NodeModel obj)
+ {
+ Console.WriteLine($"Nodes.Removed, Nodes=[{obj}]");
+ }
- private void Nodes_Added(NodeModel obj)
- {
- Console.WriteLine($"Nodes.Added, Nodes=[{obj}]");
- }
+ private void Nodes_Added(NodeModel obj)
+ {
+ Console.WriteLine($"Nodes.Added, Nodes=[{obj}]");
+ }
- private void Diagram_PanChanged()
- {
- Console.WriteLine($"PanChanged, Pan={Diagram.Pan}");
- }
+ private void Diagram_PanChanged()
+ {
+ Console.WriteLine($"PanChanged, Pan={Diagram.Pan}");
+ }
- private void Diagram_GroupRemoved(GroupModel obj)
- {
- Console.WriteLine($"GroupRemoved, Id={obj.Id}");
- }
+ private void Diagram_GroupRemoved(GroupModel obj)
+ {
+ Console.WriteLine($"GroupRemoved, Id={obj.Id}");
+ }
- private void Diagram_GroupAdded(GroupModel obj)
- {
- Console.WriteLine($"GroupAdded, Id={obj.Id}");
- }
+ private void Diagram_GroupAdded(GroupModel obj)
+ {
+ Console.WriteLine($"GroupAdded, Id={obj.Id}");
+ }
- private void Diagram_ContainerChanged()
- {
- Console.WriteLine($"ContainerChanged, Container={Diagram.Container}");
- }
+ private void Diagram_ContainerChanged()
+ {
+ Console.WriteLine($"ContainerChanged, Container={Diagram.Container}");
+ }
- private void Diagram_Changed()
- {
- Console.WriteLine("Changed");
- }
+ private void Diagram_Changed()
+ {
+ Console.WriteLine("Changed");
+ }
- public override void Dispose()
- {
- 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.Groups.Added -= Diagram_GroupAdded;
- Diagram.Groups.Removed -= Diagram_GroupRemoved;
- Diagram.SelectionChanged -= Diagram_SelectionChanged;
- Diagram.ZoomChanged -= Diagram_ZoomChanged;
- }
+ public override void Dispose()
+ {
+ 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.Groups.Added -= Diagram_GroupAdded;
+ Diagram.Groups.Removed -= Diagram_GroupRemoved;
+ Diagram.SelectionChanged -= Diagram_SelectionChanged;
+ Diagram.ZoomChanged -= Diagram_ZoomChanged;
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs
index 7d4240ba1..692a83dd1 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/DragMovablesBehavior.cs
@@ -5,115 +5,114 @@
using System.Collections.Generic;
using Blazor.Diagrams.Core.Models;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class DragMovablesBehavior : Behavior
{
- public class DragMovablesBehavior : Behavior
+ private readonly Dictionary _initialPositions;
+ private double? _lastClientX;
+ private double? _lastClientY;
+ private bool _moved;
+
+ public DragMovablesBehavior(Diagram diagram) : base(diagram)
{
- private readonly Dictionary _initialPositions;
- private double? _lastClientX;
- private double? _lastClientY;
- private bool _moved;
+ _initialPositions = new Dictionary();
+ Diagram.PointerDown += OnPointerDown;
+ Diagram.PointerMove += OnPointerMove;
+ Diagram.PointerUp += OnPointerUp;
+ }
- public DragMovablesBehavior(Diagram diagram) : base(diagram)
- {
- _initialPositions = new Dictionary();
- Diagram.PointerDown += OnPointerDown;
- Diagram.PointerMove += OnPointerMove;
- Diagram.PointerUp += OnPointerUp;
- }
+ private void OnPointerDown(Model? model, PointerEventArgs e)
+ {
+ if (model is not MovableModel)
+ return;
- private void OnPointerDown(Model? model, PointerEventArgs e)
+ _initialPositions.Clear();
+ foreach (var sm in Diagram.GetSelectedModels())
{
- if (model is not MovableModel)
- return;
-
- _initialPositions.Clear();
- foreach (var sm in Diagram.GetSelectedModels())
- {
- if (sm is not MovableModel movable || movable.Locked)
- continue;
+ if (sm is not MovableModel movable || movable.Locked)
+ continue;
- // Special case: groups without auto size on
- if (sm is NodeModel node && node.Group != null && !node.Group.AutoSize)
- continue;
+ // Special case: groups without auto size on
+ if (sm is NodeModel node && node.Group != null && !node.Group.AutoSize)
+ continue;
- var position = movable.Position;
- if (Diagram.Options.GridSnapToCenter && movable is NodeModel n)
- {
- position = new Point(movable.Position.X + (n.Size?.Width ?? 0) / 2,
- movable.Position.Y + (n.Size?.Height ?? 0) / 2);
- }
-
- _initialPositions.Add(movable, position);
+ var position = movable.Position;
+ if (Diagram.Options.GridSnapToCenter && movable is NodeModel n)
+ {
+ position = new Point(movable.Position.X + (n.Size?.Width ?? 0) / 2,
+ movable.Position.Y + (n.Size?.Height ?? 0) / 2);
}
- _lastClientX = e.ClientX;
- _lastClientY = e.ClientY;
- _moved = false;
+ _initialPositions.Add(movable, position);
}
- private void OnPointerMove(Model? model, PointerEventArgs e)
- {
- if (_initialPositions.Count == 0 || _lastClientX == null || _lastClientY == null)
- return;
+ _lastClientX = e.ClientX;
+ _lastClientY = e.ClientY;
+ _moved = false;
+ }
- _moved = true;
- var deltaX = (e.ClientX - _lastClientX.Value) / Diagram.Zoom;
- var deltaY = (e.ClientY - _lastClientY.Value) / Diagram.Zoom;
+ private void OnPointerMove(Model? model, PointerEventArgs e)
+ {
+ if (_initialPositions.Count == 0 || _lastClientX == null || _lastClientY == null)
+ return;
+
+ _moved = true;
+ var deltaX = (e.ClientX - _lastClientX.Value) / Diagram.Zoom;
+ var deltaY = (e.ClientY - _lastClientY.Value) / Diagram.Zoom;
- foreach (var (movable, initialPosition) in _initialPositions)
+ foreach (var (movable, initialPosition) in _initialPositions)
+ {
+ var ndx = ApplyGridSize(deltaX + initialPosition.X);
+ var ndy = ApplyGridSize(deltaY + initialPosition.Y);
+ if (Diagram.Options.GridSnapToCenter && movable is NodeModel node)
{
- var ndx = ApplyGridSize(deltaX + initialPosition.X);
- var ndy = ApplyGridSize(deltaY + initialPosition.Y);
- if (Diagram.Options.GridSnapToCenter && movable is NodeModel node)
- {
- node.SetPosition(ndx - (node.Size?.Width ?? 0) / 2, ndy - (node.Size?.Height ?? 0) / 2);
- }
- else
- {
- movable.SetPosition(ndx, ndy);
- }
+ node.SetPosition(ndx - (node.Size?.Width ?? 0) / 2, ndy - (node.Size?.Height ?? 0) / 2);
+ }
+ else
+ {
+ movable.SetPosition(ndx, ndy);
}
}
+ }
- private void OnPointerUp(Model? model, PointerEventArgs e)
- {
- if (_initialPositions.Count == 0)
- return;
+ private void OnPointerUp(Model? model, PointerEventArgs e)
+ {
+ if (_initialPositions.Count == 0)
+ return;
- if (_moved)
+ if (_moved)
+ {
+ foreach (var (movable, _) in _initialPositions)
{
- foreach (var (movable, _) in _initialPositions)
- {
- movable.TriggerMoved();
- }
+ movable.TriggerMoved();
}
-
- _initialPositions.Clear();
- _lastClientX = null;
- _lastClientY = null;
}
+
+ _initialPositions.Clear();
+ _lastClientX = null;
+ _lastClientY = null;
+ }
- private double ApplyGridSize(double n)
- {
- if (Diagram.Options.GridSize == null)
- return n;
+ private double ApplyGridSize(double n)
+ {
+ if (Diagram.Options.GridSize == null)
+ return n;
- var gridSize = Diagram.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
- // 20 * floor((110 + 10) / 20) = 20 * 6 = 120
- return gridSize * Math.Floor((n + gridSize / 2.0) / gridSize);
- }
+ // 20 * floor((100 + 10) / 20) = 20 * 5 = 100
+ // 20 * floor((105 + 10) / 20) = 20 * 5 = 100
+ // 20 * floor((110 + 10) / 20) = 20 * 6 = 120
+ return gridSize * Math.Floor((n + gridSize / 2.0) / gridSize);
+ }
- public override void Dispose()
- {
- _initialPositions.Clear();
-
- Diagram.PointerDown -= OnPointerDown;
- Diagram.PointerMove -= OnPointerMove;
- Diagram.PointerUp -= OnPointerUp;
- }
+ public override void Dispose()
+ {
+ _initialPositions.Clear();
+
+ Diagram.PointerDown -= OnPointerDown;
+ Diagram.PointerMove -= OnPointerMove;
+ Diagram.PointerUp -= OnPointerUp;
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs
index 2ac47210e..e50b5a9d9 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/DragNewLinkBehavior.cs
@@ -4,148 +4,147 @@
using System.Linq;
using Blazor.Diagrams.Core.Anchors;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class DragNewLinkBehavior : Behavior
{
- public class DragNewLinkBehavior : Behavior
+ private BaseLinkModel? _ongoingLink;
+ private PositionAnchor? _targetPositionAnchor;
+
+ public DragNewLinkBehavior(Diagram diagram) : base(diagram)
{
- private BaseLinkModel? _ongoingLink;
- private PositionAnchor? _targetPositionAnchor;
+ Diagram.PointerDown += OnPointerDown;
+ Diagram.PointerMove += OnPointerMove;
+ Diagram.PointerUp += OnPointerUp;
+ }
- public DragNewLinkBehavior(Diagram diagram) : base(diagram)
- {
- Diagram.PointerDown += OnPointerDown;
- Diagram.PointerMove += OnPointerMove;
- Diagram.PointerUp += OnPointerUp;
- }
+ public void StartFrom(ILinkable source, double clientX, double clientY)
+ {
+ if (_ongoingLink != null)
+ return;
+
+ _targetPositionAnchor = new PositionAnchor(Diagram.GetRelativeMousePoint(clientX, clientY).Substract(5));
+ _ongoingLink = Diagram.Options.Links.Factory(Diagram, source, _targetPositionAnchor);
+ if (_ongoingLink == null)
+ return;
+
+ Diagram.Links.Add(_ongoingLink);
+ }
- public void StartFrom(ILinkable source, double clientX, double clientY)
+ public void StartFrom(BaseLinkModel link, double clientX, double clientY)
+ {
+ if (_ongoingLink != null)
+ return;
+
+ _targetPositionAnchor = new PositionAnchor(Diagram.GetRelativeMousePoint(clientX, clientY).Substract(5));
+ _ongoingLink = link;
+ _ongoingLink.SetTarget(_targetPositionAnchor);
+ _ongoingLink.Refresh();
+ _ongoingLink.RefreshLinks();
+ }
+
+ private void OnPointerDown(Model? model, MouseEventArgs e)
+ {
+ if (e.Button != (int)MouseEventButton.Left)
+ return;
+
+ _ongoingLink = null;
+ _targetPositionAnchor = null;
+
+ if (model is PortModel port)
{
- if (_ongoingLink != null)
+ if (port.Locked)
return;
- _targetPositionAnchor = new PositionAnchor(Diagram.GetRelativeMousePoint(clientX, clientY).Substract(5));
- _ongoingLink = Diagram.Options.Links.Factory(Diagram, source, _targetPositionAnchor);
+ _targetPositionAnchor = new PositionAnchor(Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY).Substract(5));
+ _ongoingLink = Diagram.Options.Links.Factory(Diagram, port, _targetPositionAnchor);
if (_ongoingLink == null)
return;
+ _ongoingLink.SetTarget(_targetPositionAnchor);
Diagram.Links.Add(_ongoingLink);
}
+ }
- public void StartFrom(BaseLinkModel link, double clientX, double clientY)
- {
- if (_ongoingLink != null)
- return;
+ private void OnPointerMove(Model? model, MouseEventArgs e)
+ {
+ if (_ongoingLink == null || model != null)
+ return;
- _targetPositionAnchor = new PositionAnchor(Diagram.GetRelativeMousePoint(clientX, clientY).Substract(5));
- _ongoingLink = link;
- _ongoingLink.SetTarget(_targetPositionAnchor);
- _ongoingLink.Refresh();
- _ongoingLink.RefreshLinks();
- }
+ _targetPositionAnchor!.SetPosition(Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY).Substract(5));
- private void OnPointerDown(Model? model, MouseEventArgs e)
+ if (Diagram.Options.Links.EnableSnapping)
{
- if (e.Button != (int)MouseEventButton.Left)
- return;
-
- _ongoingLink = null;
- _targetPositionAnchor = null;
-
- if (model is PortModel port)
+ var nearPort = FindNearPortToAttachTo();
+ if (nearPort != null || _ongoingLink.Target is not PositionAnchor)
{
- if (port.Locked)
- return;
-
- _targetPositionAnchor = new PositionAnchor(Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY).Substract(5));
- _ongoingLink = Diagram.Options.Links.Factory(Diagram, port, _targetPositionAnchor);
- if (_ongoingLink == null)
- return;
-
- _ongoingLink.SetTarget(_targetPositionAnchor);
- Diagram.Links.Add(_ongoingLink);
+ _ongoingLink.SetTarget(nearPort is null ? _targetPositionAnchor : new SinglePortAnchor(nearPort));
}
}
- private void OnPointerMove(Model? model, MouseEventArgs e)
- {
- if (_ongoingLink == null || model != null)
- return;
+ _ongoingLink.Refresh();
+ _ongoingLink.RefreshLinks();
+ }
- _targetPositionAnchor!.SetPosition(Diagram.GetRelativeMousePoint(e.ClientX, e.ClientY).Substract(5));
+ private void OnPointerUp(Model? model, MouseEventArgs e)
+ {
+ if (_ongoingLink == null)
+ return;
- if (Diagram.Options.Links.EnableSnapping)
- {
- var nearPort = FindNearPortToAttachTo();
- if (nearPort != null || _ongoingLink.Target is not PositionAnchor)
- {
- _ongoingLink.SetTarget(nearPort is null ? _targetPositionAnchor : new SinglePortAnchor(nearPort));
- }
- }
+ if (_ongoingLink.IsAttached) // Snapped already
+ {
+ _ongoingLink.TriggerTargetAttached();
+ _ongoingLink = null;
+ return;
+ }
+ if (model is ILinkable linkable && (_ongoingLink.Source.Model == null || _ongoingLink.Source.Model.CanAttachTo(linkable)))
+ {
+ var targetAnchor = Diagram.Options.Links.TargetAnchorFactory(Diagram, _ongoingLink, linkable);
+ _ongoingLink.SetTarget(targetAnchor);
+ _ongoingLink.TriggerTargetAttached();
_ongoingLink.Refresh();
_ongoingLink.RefreshLinks();
}
-
- private void OnPointerUp(Model? model, MouseEventArgs e)
+ else if (Diagram.Options.Links.RequireTarget)
{
- if (_ongoingLink == null)
- return;
-
- if (_ongoingLink.IsAttached) // Snapped already
- {
- _ongoingLink.TriggerTargetAttached();
- _ongoingLink = null;
- return;
- }
+ Diagram.Links.Remove(_ongoingLink);
+ }
- if (model is ILinkable linkable && (_ongoingLink.Source.Model == null || _ongoingLink.Source.Model.CanAttachTo(linkable)))
- {
- var targetAnchor = Diagram.Options.Links.TargetAnchorFactory(Diagram, _ongoingLink, linkable);
- _ongoingLink.SetTarget(targetAnchor);
- _ongoingLink.TriggerTargetAttached();
- _ongoingLink.Refresh();
- _ongoingLink.RefreshLinks();
- }
- else if (Diagram.Options.Links.RequireTarget)
- {
- Diagram.Links.Remove(_ongoingLink);
- }
+ _ongoingLink = null;
+ }
- _ongoingLink = null;
- }
+ private PortModel? FindNearPortToAttachTo()
+ {
+ if (_ongoingLink is null || _targetPositionAnchor is null)
+ return null;
- private PortModel? FindNearPortToAttachTo()
- {
- if (_ongoingLink is null || _targetPositionAnchor is null)
- return null;
+ PortModel? nearestSnapPort = null;
+ var nearestSnapPortDistance = double.PositiveInfinity;
- PortModel? nearestSnapPort = null;
- var nearestSnapPortDistance = double.PositiveInfinity;
+ var position = _targetPositionAnchor!.GetPosition(_ongoingLink)!;
- var position = _targetPositionAnchor!.GetPosition(_ongoingLink)!;
+ foreach (var port in Diagram.Nodes.SelectMany((NodeModel n) => n.Ports))
+ {
+ var distance = position.DistanceTo(port.Position);
- foreach (var port in Diagram.Nodes.SelectMany((NodeModel n) => n.Ports))
+ if (distance <= Diagram.Options.Links.SnappingRadius && (_ongoingLink.Source.Model?.CanAttachTo(port) != false))
{
- var distance = position.DistanceTo(port.Position);
-
- if (distance <= Diagram.Options.Links.SnappingRadius && (_ongoingLink.Source.Model?.CanAttachTo(port) != false))
+ if (distance < nearestSnapPortDistance)
{
- if (distance < nearestSnapPortDistance)
- {
- nearestSnapPortDistance = distance;
- nearestSnapPort = port;
- }
+ nearestSnapPortDistance = distance;
+ nearestSnapPort = port;
}
}
-
- return nearestSnapPort;
}
- public override void Dispose()
- {
- Diagram.PointerDown -= OnPointerDown;
- Diagram.PointerMove -= OnPointerMove;
- Diagram.PointerUp -= OnPointerUp;
- }
+ return nearestSnapPort;
+ }
+
+ public override void Dispose()
+ {
+ Diagram.PointerDown -= OnPointerDown;
+ Diagram.PointerMove -= OnPointerMove;
+ Diagram.PointerUp -= OnPointerUp;
}
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs
index 107659baf..6bb8bc0c7 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/EventsBehavior.cs
@@ -2,70 +2,69 @@
using Blazor.Diagrams.Core.Events;
using System.Diagnostics;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class EventsBehavior : Behavior
{
- public class EventsBehavior : Behavior
- {
- private readonly Stopwatch _mouseClickSw;
- private Model? _model;
- private bool _captureMouseMove;
- private int _mouseMovedCount;
+ private readonly Stopwatch _mouseClickSw;
+ private Model? _model;
+ private bool _captureMouseMove;
+ private int _mouseMovedCount;
- public EventsBehavior(Diagram diagram) : base(diagram)
- {
- _mouseClickSw = new Stopwatch();
+ public EventsBehavior(Diagram diagram) : base(diagram)
+ {
+ _mouseClickSw = new Stopwatch();
- Diagram.PointerDown += OnPointerDown;
- Diagram.PointerMove += OnPointerMove;
- Diagram.PointerUp += OnPointerUp;
- Diagram.PointerClick += OnPointerClick;
- }
+ Diagram.PointerDown += OnPointerDown;
+ Diagram.PointerMove += OnPointerMove;
+ Diagram.PointerUp += OnPointerUp;
+ Diagram.PointerClick += OnPointerClick;
+ }
- private void OnPointerClick(Model? model, PointerEventArgs e)
+ private void OnPointerClick(Model? model, PointerEventArgs e)
+ {
+ if (_mouseClickSw.IsRunning && _mouseClickSw.ElapsedMilliseconds <= 500)
{
- if (_mouseClickSw.IsRunning && _mouseClickSw.ElapsedMilliseconds <= 500)
- {
- Diagram.TriggerPointerDoubleClick(model, e);
- }
-
- _mouseClickSw.Restart();
+ Diagram.TriggerPointerDoubleClick(model, e);
}
- private void OnPointerDown(Model? model, PointerEventArgs e)
- {
- _captureMouseMove = true;
- _mouseMovedCount = 0;
- _model = model;
- }
+ _mouseClickSw.Restart();
+ }
- private void OnPointerMove(Model? model, PointerEventArgs e)
- {
- if (!_captureMouseMove)
- return;
+ private void OnPointerDown(Model? model, PointerEventArgs e)
+ {
+ _captureMouseMove = true;
+ _mouseMovedCount = 0;
+ _model = model;
+ }
- _mouseMovedCount++;
- }
+ private void OnPointerMove(Model? model, PointerEventArgs e)
+ {
+ if (!_captureMouseMove)
+ return;
- private void OnPointerUp(Model? model, PointerEventArgs e)
- {
- if (!_captureMouseMove) return; // Only set by OnMouseDown
- _captureMouseMove = false;
- if (_mouseMovedCount > 0) return;
+ _mouseMovedCount++;
+ }
- if (_model == model)
- {
- Diagram.TriggerPointerClick(model, e);
- _model = null;
- }
- }
+ private void OnPointerUp(Model? model, PointerEventArgs e)
+ {
+ if (!_captureMouseMove) return; // Only set by OnMouseDown
+ _captureMouseMove = false;
+ if (_mouseMovedCount > 0) return;
- public override void Dispose()
+ if (_model == model)
{
- Diagram.PointerDown -= OnPointerDown;
- Diagram.PointerMove -= OnPointerMove;
- Diagram.PointerUp -= OnPointerUp;
- Diagram.PointerClick -= OnPointerClick;
+ Diagram.TriggerPointerClick(model, e);
_model = null;
}
}
+
+ public override void Dispose()
+ {
+ Diagram.PointerDown -= OnPointerDown;
+ Diagram.PointerMove -= OnPointerMove;
+ Diagram.PointerUp -= OnPointerUp;
+ Diagram.PointerClick -= OnPointerClick;
+ _model = null;
+ }
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsBehavior.cs
index c48293617..5243bed09 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsBehavior.cs
@@ -4,45 +4,44 @@
using System.Collections.Generic;
using System.Threading.Tasks;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class KeyboardShortcutsBehavior : Behavior
{
- public class KeyboardShortcutsBehavior : Behavior
- {
- private readonly Dictionary> _shortcuts;
+ private readonly Dictionary> _shortcuts;
- public KeyboardShortcutsBehavior(Diagram diagram) : base(diagram)
- {
- _shortcuts = new Dictionary>();
- SetShortcut("Delete", false, false, false, KeyboardShortcutsDefaults.DeleteSelection);
- SetShortcut("g", true, false, true, KeyboardShortcutsDefaults.Grouping);
+ public KeyboardShortcutsBehavior(Diagram diagram) : base(diagram)
+ {
+ _shortcuts = new Dictionary>();
+ SetShortcut("Delete", false, false, false, KeyboardShortcutsDefaults.DeleteSelection);
+ SetShortcut("g", true, false, true, KeyboardShortcutsDefaults.Grouping);
- Diagram.KeyDown += OnDiagramKeyDown;
- }
+ Diagram.KeyDown += OnDiagramKeyDown;
+ }
- public void SetShortcut(string key, bool ctrl, bool shift, bool alt, Func action)
- {
- var k = KeysUtils.GetStringRepresentation(ctrl, shift, alt, key);
- _shortcuts[k] = action;
- }
+ public void SetShortcut(string key, bool ctrl, bool shift, bool alt, Func action)
+ {
+ var k = KeysUtils.GetStringRepresentation(ctrl, shift, alt, key);
+ _shortcuts[k] = action;
+ }
- public bool RemoveShortcut(string key, bool ctrl, bool shift, bool alt)
- {
- var k = KeysUtils.GetStringRepresentation(ctrl, shift, alt, key);
- return _shortcuts.Remove(k);
- }
+ public bool RemoveShortcut(string key, bool ctrl, bool shift, bool alt)
+ {
+ var k = KeysUtils.GetStringRepresentation(ctrl, shift, alt, key);
+ return _shortcuts.Remove(k);
+ }
- private async void OnDiagramKeyDown(KeyboardEventArgs e)
+ private async void OnDiagramKeyDown(KeyboardEventArgs e)
+ {
+ var k = KeysUtils.GetStringRepresentation(e.CtrlKey, e.ShiftKey, e.AltKey, e.Key);
+ if (_shortcuts.TryGetValue(k, out var action))
{
- var k = KeysUtils.GetStringRepresentation(e.CtrlKey, e.ShiftKey, e.AltKey, e.Key);
- if (_shortcuts.TryGetValue(k, out var action))
- {
- await action(Diagram);
- }
+ await action(Diagram);
}
+ }
- public override void Dispose()
- {
- Diagram.KeyDown -= OnDiagramKeyDown;
- }
+ public override void Dispose()
+ {
+ Diagram.KeyDown -= OnDiagramKeyDown;
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsDefaults.cs b/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsDefaults.cs
index 4c8fa1786..0c338114a 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsDefaults.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/KeyboardShortcutsDefaults.cs
@@ -3,72 +3,71 @@
using System.Linq;
using System.Threading.Tasks;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public static class KeyboardShortcutsDefaults
{
- public static class KeyboardShortcutsDefaults
+ public static async ValueTask DeleteSelection(Diagram diagram)
{
- public static async ValueTask DeleteSelection(Diagram diagram)
+ var wasSuspended = diagram.SuspendRefresh;
+ if (!wasSuspended) diagram.SuspendRefresh = true;
+
+ foreach (var sm in diagram.GetSelectedModels().ToArray())
{
- var wasSuspended = diagram.SuspendRefresh;
- if (!wasSuspended) diagram.SuspendRefresh = true;
+ if (sm.Locked)
+ continue;
- foreach (var sm in diagram.GetSelectedModels().ToArray())
+ if (sm is GroupModel group && (await diagram.Options.Constraints.ShouldDeleteGroup(group)))
{
- if (sm.Locked)
- continue;
-
- if (sm is GroupModel group && (await diagram.Options.Constraints.ShouldDeleteGroup(group)))
- {
- diagram.Groups.Delete(group);
- }
- else if (sm is NodeModel node && (await diagram.Options.Constraints.ShouldDeleteNode(node)))
- {
- diagram.Nodes.Remove(node);
- }
- else if (sm is BaseLinkModel link && (await diagram.Options.Constraints.ShouldDeleteLink(link)))
- {
- diagram.Links.Remove(link);
- }
+ diagram.Groups.Delete(group);
}
-
- if (!wasSuspended)
+ else if (sm is NodeModel node && (await diagram.Options.Constraints.ShouldDeleteNode(node)))
+ {
+ diagram.Nodes.Remove(node);
+ }
+ else if (sm is BaseLinkModel link && (await diagram.Options.Constraints.ShouldDeleteLink(link)))
{
- diagram.SuspendRefresh = false;
- diagram.Refresh();
+ diagram.Links.Remove(link);
}
}
- public static ValueTask Grouping(Diagram diagram)
+ if (!wasSuspended)
{
- if (!diagram.Options.Groups.Enabled)
- return ValueTask.CompletedTask;
+ diagram.SuspendRefresh = false;
+ diagram.Refresh();
+ }
+ }
- if (!diagram.GetSelectedModels().Any())
- return ValueTask.CompletedTask;
+ public static ValueTask Grouping(Diagram diagram)
+ {
+ if (!diagram.Options.Groups.Enabled)
+ return ValueTask.CompletedTask;
- var selectedNodes = diagram.Nodes.Where(n => n.Selected).ToArray();
- var nodesWithGroup = selectedNodes.Where(n => n.Group != null).ToArray();
- if (nodesWithGroup.Length > 0)
+ if (!diagram.GetSelectedModels().Any())
+ return ValueTask.CompletedTask;
+
+ 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))
{
- // Ungroup
- foreach (var group in nodesWithGroup.GroupBy(n => n.Group!).Select(g => g.Key))
- {
- diagram.Groups.Remove(group);
- }
+ diagram.Groups.Remove(group);
}
- else
- {
- // Group
- if (selectedNodes.Length < 2)
- return ValueTask.CompletedTask;
-
- if (selectedNodes.Any(n => n.Group != null))
- return ValueTask.CompletedTask;
+ }
+ else
+ {
+ // Group
+ if (selectedNodes.Length < 2)
+ return ValueTask.CompletedTask;
- diagram.Groups.Group(selectedNodes);
- }
+ if (selectedNodes.Any(n => n.Group != null))
+ return ValueTask.CompletedTask;
- return ValueTask.CompletedTask;
+ diagram.Groups.Group(selectedNodes);
}
+
+ return ValueTask.CompletedTask;
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs
index 3e94b9d58..bb4d974b7 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/PanBehavior.cs
@@ -2,66 +2,65 @@
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Core.Events;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class PanBehavior : Behavior
{
- public class PanBehavior : Behavior
- {
- private Point? _initialPan;
- private double _lastClientX;
- private double _lastClientY;
+ private Point? _initialPan;
+ private double _lastClientX;
+ private double _lastClientY;
- public PanBehavior(Diagram diagram) : base(diagram)
- {
- Diagram.PointerDown += OnPointerDown;
- Diagram.PointerMove += OnPointerMove;
- Diagram.PointerUp += OnPointerUp;
- }
+ public PanBehavior(Diagram diagram) : base(diagram)
+ {
+ Diagram.PointerDown += OnPointerDown;
+ Diagram.PointerMove += OnPointerMove;
+ Diagram.PointerUp += OnPointerUp;
+ }
- private void OnPointerDown(Model? model, PointerEventArgs e)
- {
- if (e.Button != (int)MouseEventButton.Left)
- return;
+ private void OnPointerDown(Model? model, PointerEventArgs e)
+ {
+ if (e.Button != (int)MouseEventButton.Left)
+ return;
- Start(model, e.ClientX, e.ClientY, e.ShiftKey);
- }
+ Start(model, e.ClientX, e.ClientY, e.ShiftKey);
+ }
- private void OnPointerMove(Model? model, PointerEventArgs e) => Move(e.ClientX, e.ClientY);
+ private void OnPointerMove(Model? model, PointerEventArgs e) => Move(e.ClientX, e.ClientY);
- private void OnPointerUp(Model? model, PointerEventArgs e) => End();
+ private void OnPointerUp(Model? model, PointerEventArgs e) => End();
- private void Start(Model? model, double clientX, double clientY, bool shiftKey)
- {
- if (!Diagram.Options.AllowPanning || model != null || shiftKey)
- return;
+ private void Start(Model? model, double clientX, double clientY, bool shiftKey)
+ {
+ if (!Diagram.Options.AllowPanning || model != null || shiftKey)
+ return;
- _initialPan = Diagram.Pan;
- _lastClientX = clientX;
- _lastClientY = clientY;
- }
+ _initialPan = Diagram.Pan;
+ _lastClientX = clientX;
+ _lastClientY = clientY;
+ }
- private void Move(double clientX, double clientY)
- {
- if (!Diagram.Options.AllowPanning || _initialPan == null)
- return;
+ private void Move(double clientX, double clientY)
+ {
+ if (!Diagram.Options.AllowPanning || _initialPan == null)
+ return;
- var deltaX = clientX - _lastClientX - (Diagram.Pan.X - _initialPan.X);
- var deltaY = clientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y);
- Diagram.UpdatePan(deltaX, deltaY);
- }
+ var deltaX = clientX - _lastClientX - (Diagram.Pan.X - _initialPan.X);
+ var deltaY = clientY - _lastClientY - (Diagram.Pan.Y - _initialPan.Y);
+ Diagram.UpdatePan(deltaX, deltaY);
+ }
- private void End()
- {
- if (!Diagram.Options.AllowPanning)
- return;
+ private void End()
+ {
+ if (!Diagram.Options.AllowPanning)
+ return;
- _initialPan = null;
- }
+ _initialPan = null;
+ }
- public override void Dispose()
- {
- Diagram.PointerDown -= OnPointerDown;
- Diagram.PointerMove -= OnPointerMove;
- Diagram.PointerUp -= OnPointerUp;
- }
+ public override void Dispose()
+ {
+ Diagram.PointerDown -= OnPointerDown;
+ Diagram.PointerMove -= OnPointerMove;
+ Diagram.PointerUp -= OnPointerUp;
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs
index 016427eb8..8c3d75ab8 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/SelectionBehavior.cs
@@ -1,41 +1,40 @@
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Core.Events;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class SelectionBehavior : Behavior
{
- public class SelectionBehavior : Behavior
+ public SelectionBehavior(Diagram diagram) : base(diagram)
{
- public SelectionBehavior(Diagram diagram) : base(diagram)
- {
- Diagram.PointerDown += OnPointerDown;
- }
+ Diagram.PointerDown += OnPointerDown;
+ }
- private void OnPointerDown(Model? model, PointerEventArgs e)
+ private void OnPointerDown(Model? model, PointerEventArgs e)
+ {
+ var ctrlKey = e.CtrlKey;
+ switch (model)
{
- var ctrlKey = e.CtrlKey;
- switch (model)
+ case null:
+ Diagram.UnselectAll();
+ break;
+ case SelectableModel sm when ctrlKey && sm.Selected:
+ Diagram.UnselectModel(sm);
+ break;
+ case SelectableModel sm:
{
- case null:
- Diagram.UnselectAll();
- break;
- case SelectableModel sm when ctrlKey && sm.Selected:
- Diagram.UnselectModel(sm);
- break;
- case SelectableModel sm:
+ if (!sm.Selected)
{
- if (!sm.Selected)
- {
- Diagram.SelectModel(sm, !ctrlKey || !Diagram.Options.AllowMultiSelection);
- }
-
- break;
+ Diagram.SelectModel(sm, !ctrlKey || !Diagram.Options.AllowMultiSelection);
}
+
+ break;
}
}
+ }
- public override void Dispose()
- {
- Diagram.PointerDown -= OnPointerDown;
- }
+ public override void Dispose()
+ {
+ Diagram.PointerDown -= OnPointerDown;
}
}
diff --git a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs
index c145d35e3..ee6cca73e 100644
--- a/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs
+++ b/src/Blazor.Diagrams.Core/Behaviors/ZoomBehavior.cs
@@ -4,55 +4,54 @@
using System;
-namespace Blazor.Diagrams.Core.Behaviors
+namespace Blazor.Diagrams.Core.Behaviors;
+
+public class ZoomBehavior : Behavior
{
- public class ZoomBehavior : Behavior
+ public ZoomBehavior(Diagram diagram) : base(diagram)
{
- public ZoomBehavior(Diagram diagram) : base(diagram)
- {
- Diagram.Wheel += Diagram_Wheel;
- }
+ Diagram.Wheel += Diagram_Wheel;
+ }
- private void Diagram_Wheel(WheelEventArgs e)
- {
- if (Diagram.Container == null || e.DeltaY == 0)
- return;
-
- if (!Diagram.Options.Zoom.Enabled)
- return;
-
- var scale = Diagram.Options.Zoom.ScaleFactor;
- var oldZoom = Diagram.Zoom;
- var deltaY = Diagram.Options.Zoom.Inverse ? e.DeltaY * -1 : e.DeltaY;
- var newZoom = deltaY > 0 ? oldZoom * scale : oldZoom / scale;
- newZoom = Math.Clamp(newZoom, Diagram.Options.Zoom.Minimum, Diagram.Options.Zoom.Maximum);
-
- if (newZoom < 0 || newZoom == Diagram.Zoom)
- return;
-
- // 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 = Diagram.Container.Width;
- var clientHeight = Diagram.Container.Height;
- var widthDiff = clientWidth * newZoom - clientWidth * oldZoom;
- var heightDiff = clientHeight * newZoom - clientHeight * oldZoom;
- 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;
-
- Diagram.Batch(() =>
- {
- Diagram.SetPan(newPanX, newPanY);
- Diagram.SetZoom(newZoom);
- });
- }
-
- public override void Dispose()
+ private void Diagram_Wheel(WheelEventArgs e)
+ {
+ if (Diagram.Container == null || e.DeltaY == 0)
+ return;
+
+ if (!Diagram.Options.Zoom.Enabled)
+ return;
+
+ var scale = Diagram.Options.Zoom.ScaleFactor;
+ var oldZoom = Diagram.Zoom;
+ var deltaY = Diagram.Options.Zoom.Inverse ? e.DeltaY * -1 : e.DeltaY;
+ var newZoom = deltaY > 0 ? oldZoom * scale : oldZoom / scale;
+ newZoom = Math.Clamp(newZoom, Diagram.Options.Zoom.Minimum, Diagram.Options.Zoom.Maximum);
+
+ if (newZoom < 0 || newZoom == Diagram.Zoom)
+ return;
+
+ // 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 = Diagram.Container.Width;
+ var clientHeight = Diagram.Container.Height;
+ var widthDiff = clientWidth * newZoom - clientWidth * oldZoom;
+ var heightDiff = clientHeight * newZoom - clientHeight * oldZoom;
+ 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;
+
+ Diagram.Batch(() =>
{
- Diagram.Wheel -= Diagram_Wheel;
- }
+ Diagram.SetPan(newPanX, newPanY);
+ Diagram.SetZoom(newZoom);
+ });
+ }
+
+ public override void Dispose()
+ {
+ Diagram.Wheel -= Diagram_Wheel;
}
}
diff --git a/src/Blazor.Diagrams.Core/Controls/Default/ArrowHeadControl.cs b/src/Blazor.Diagrams.Core/Controls/Default/ArrowHeadControl.cs
index 0fccc3911..802bd6178 100644
--- a/src/Blazor.Diagrams.Core/Controls/Default/ArrowHeadControl.cs
+++ b/src/Blazor.Diagrams.Core/Controls/Default/ArrowHeadControl.cs
@@ -7,53 +7,52 @@
using System;
using System.Threading.Tasks;
-namespace Blazor.Diagrams.Core.Controls.Default
+namespace Blazor.Diagrams.Core.Controls.Default;
+
+public class ArrowHeadControl : ExecutableControl
{
- public class ArrowHeadControl : ExecutableControl
+ public ArrowHeadControl(bool source, LinkMarker? marker = null)
{
- public ArrowHeadControl(bool source, LinkMarker? marker = null)
- {
- Source = source;
- Marker = marker ?? LinkMarker.NewArrow(20, 20);
- }
+ Source = source;
+ Marker = marker ?? LinkMarker.NewArrow(20, 20);
+ }
- public bool Source { get; }
- public LinkMarker Marker { get; }
- public double Angle { get; private set; }
+ public bool Source { get; }
+ public LinkMarker Marker { get; }
+ public double Angle { get; private set; }
- public override Point? GetPosition(Model model)
- {
- if (model is not BaseLinkModel link)
- throw new DiagramsException("ArrowHeadControl only works for models of type BaseLinkModel");
+ public override Point? GetPosition(Model model)
+ {
+ if (model is not BaseLinkModel link)
+ throw new DiagramsException("ArrowHeadControl only works for models of type BaseLinkModel");
- var dist = Source ? Marker.Width - (link.SourceMarker?.Width ?? 0) : (link.TargetMarker?.Width ?? 0) - Marker.Width;
- var pp = new LinkPathPositionProvider(dist);
- var p1 = pp.GetPosition(link);
- if (p1 is not null)
+ var dist = Source ? Marker.Width - (link.SourceMarker?.Width ?? 0) : (link.TargetMarker?.Width ?? 0) - Marker.Width;
+ var pp = new LinkPathPositionProvider(dist);
+ var p1 = pp.GetPosition(link);
+ if (p1 is not null)
+ {
+ var p2 = Source ? link.Source.GetPosition(link) : link.Target.GetPosition(link);
+ if (p2 is not null)
{
- var p2 = Source ? link.Source.GetPosition(link) : link.Target.GetPosition(link);
- if (p2 is not null)
- {
- Angle = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X) * 180 / Math.PI;
- }
+ Angle = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X) * 180 / Math.PI;
}
-
- return p1;
}
- public override ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs e)
- {
- if (model is not BaseLinkModel link)
- throw new DiagramsException("ArrowHeadControl only works for models of type BaseLinkModel");
+ return p1;
+ }
- var dnlb = diagram.GetBehavior()!;
- if (Source)
- {
- link.SetSource(link.Target);
- }
+ public override ValueTask OnPointerDown(Diagram diagram, Model model, PointerEventArgs e)
+ {
+ if (model is not BaseLinkModel link)
+ throw new DiagramsException("ArrowHeadControl only works for models of type BaseLinkModel");
- dnlb.StartFrom(link, e.ClientX, e.ClientY);
- return ValueTask.CompletedTask;
+ var dnlb = diagram.GetBehavior()!;
+ if (Source)
+ {
+ link.SetSource(link.Target);
}
+
+ dnlb.StartFrom(link, e.ClientX, e.ClientY);
+ return ValueTask.CompletedTask;
}
}
diff --git a/src/Blazor.Diagrams.Core/Delegates.cs b/src/Blazor.Diagrams.Core/Delegates.cs
index b71be543c..4a128a2d3 100644
--- a/src/Blazor.Diagrams.Core/Delegates.cs
+++ b/src/Blazor.Diagrams.Core/Delegates.cs
@@ -2,11 +2,10 @@
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core
-{
- public delegate BaseLinkModel? LinkFactory(Diagram diagram, ILinkable source, Anchor targetAnchor);
+namespace Blazor.Diagrams.Core;
- public delegate Anchor AnchorFactory(Diagram diagram, BaseLinkModel link, ILinkable model);
+public delegate BaseLinkModel? LinkFactory(Diagram diagram, ILinkable source, Anchor targetAnchor);
- public delegate GroupModel GroupFactory(Diagram diagram, NodeModel[] children);
-}
+public delegate Anchor AnchorFactory(Diagram diagram, BaseLinkModel link, ILinkable model);
+
+public delegate GroupModel GroupFactory(Diagram diagram, NodeModel[] children);
diff --git a/src/Blazor.Diagrams.Core/Diagram.cs b/src/Blazor.Diagrams.Core/Diagram.cs
index 3284429ba..a2fffeacb 100644
--- a/src/Blazor.Diagrams.Core/Diagram.cs
+++ b/src/Blazor.Diagrams.Core/Diagram.cs
@@ -15,393 +15,392 @@
[assembly: InternalsVisibleTo("Blazor.Diagrams.Tests")]
[assembly: InternalsVisibleTo("Blazor.Diagrams.Core.Tests")]
-namespace Blazor.Diagrams.Core
+namespace Blazor.Diagrams.Core;
+
+public abstract class Diagram
{
- public abstract class Diagram
+ private readonly Dictionary _behaviors;
+ private readonly List _orderedSelectables;
+
+ public event Action? PointerDown;
+ public event Action? PointerMove;
+ public event Action? PointerUp;
+ public event Action? PointerEnter;
+ public event Action? PointerLeave;
+ public event Action? KeyDown;
+ public event Action? Wheel;
+ public event Action? PointerClick;
+ public event Action? PointerDoubleClick;
+
+ public event Action? SelectionChanged;
+ public event Action? PanChanged;
+ public event Action? ZoomChanged;
+ public event Action? ContainerChanged;
+ public event Action? Changed;
+
+ protected Diagram()
{
- private readonly Dictionary _behaviors;
- private readonly List _orderedSelectables;
-
- public event Action? PointerDown;
- public event Action? PointerMove;
- public event Action? PointerUp;
- public event Action? PointerEnter;
- public event Action? PointerLeave;
- public event Action? KeyDown;
- public event Action? Wheel;
- public event Action? PointerClick;
- public event Action? PointerDoubleClick;
-
- public event Action? SelectionChanged;
- public event Action? PanChanged;
- public event Action? ZoomChanged;
- public event Action? ContainerChanged;
- public event Action? Changed;
-
- protected Diagram()
- {
- _behaviors = new Dictionary();
- _orderedSelectables = new List();
-
- Nodes = new NodeLayer(this);
- Links = new LinkLayer(this);
- Groups = new GroupLayer(this);
- Controls = new ControlsLayer();
-
- Nodes.Added += OnSelectableAdded;
- Links.Added += OnSelectableAdded;
- Groups.Added += OnSelectableAdded;
-
- Nodes.Removed += OnSelectableRemoved;
- Links.Removed += OnSelectableRemoved;
- Groups.Removed += OnSelectableRemoved;
-
- RegisterBehavior(new SelectionBehavior(this));
- RegisterBehavior(new DragMovablesBehavior(this));
- RegisterBehavior(new DragNewLinkBehavior(this));
- RegisterBehavior(new PanBehavior(this));
- RegisterBehavior(new ZoomBehavior(this));
- RegisterBehavior(new EventsBehavior(this));
- RegisterBehavior(new KeyboardShortcutsBehavior(this));
- RegisterBehavior(new ControlsBehavior(this));
- RegisterBehavior(new VirtualizationBehavior(this));
- }
+ _behaviors = new Dictionary();
+ _orderedSelectables = new List();
+
+ Nodes = new NodeLayer(this);
+ Links = new LinkLayer(this);
+ Groups = new GroupLayer(this);
+ Controls = new ControlsLayer();
+
+ Nodes.Added += OnSelectableAdded;
+ Links.Added += OnSelectableAdded;
+ Groups.Added += OnSelectableAdded;
+
+ Nodes.Removed += OnSelectableRemoved;
+ Links.Removed += OnSelectableRemoved;
+ Groups.Removed += OnSelectableRemoved;
+
+ RegisterBehavior(new SelectionBehavior(this));
+ RegisterBehavior(new DragMovablesBehavior(this));
+ RegisterBehavior(new DragNewLinkBehavior(this));
+ RegisterBehavior(new PanBehavior(this));
+ RegisterBehavior(new ZoomBehavior(this));
+ RegisterBehavior(new EventsBehavior(this));
+ RegisterBehavior(new KeyboardShortcutsBehavior(this));
+ RegisterBehavior(new ControlsBehavior(this));
+ RegisterBehavior(new VirtualizationBehavior(this));
+ }
- public abstract DiagramOptions Options { get; }
- public NodeLayer Nodes { get; }
- public LinkLayer Links { get; }
- public GroupLayer Groups { get; }
- public ControlsLayer Controls { get; }
- public Rectangle? Container { get; private set; }
- public Point Pan { get; private set; } = Point.Zero;
- public double Zoom { get; private set; } = 1;
- public bool SuspendRefresh { get; set; }
- public bool SuspendSorting { get; set; }
- public IReadOnlyList OrderedSelectables => _orderedSelectables;
-
- public void Refresh()
- {
- if (SuspendRefresh)
- return;
+ public abstract DiagramOptions Options { get; }
+ public NodeLayer Nodes { get; }
+ public LinkLayer Links { get; }
+ public GroupLayer Groups { get; }
+ public ControlsLayer Controls { get; }
+ public Rectangle? Container { get; private set; }
+ public Point Pan { get; private set; } = Point.Zero;
+ public double Zoom { get; private set; } = 1;
+ public bool SuspendRefresh { get; set; }
+ public bool SuspendSorting { get; set; }
+ public IReadOnlyList OrderedSelectables => _orderedSelectables;
+
+ public void Refresh()
+ {
+ if (SuspendRefresh)
+ return;
- Changed?.Invoke();
- }
+ Changed?.Invoke();
+ }
- public void Batch(Action action)
+ public void Batch(Action action)
+ {
+ if (SuspendRefresh)
{
- if (SuspendRefresh)
- {
- // 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;
+ // If it's already suspended, just execute the action and leave it suspended
+ // It's probably handled by an outer batch
action();
- SuspendRefresh = false;
- Refresh();
+ return;
}
- #region Selection
+ SuspendRefresh = true;
+ action();
+ SuspendRefresh = false;
+ Refresh();
+ }
- public IEnumerable GetSelectedModels()
+ #region Selection
+
+ public IEnumerable GetSelectedModels()
+ {
+ foreach (var node in Nodes)
{
- foreach (var node in Nodes)
- {
- if (node.Selected)
- yield return node;
- }
+ 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 link in Links)
+ {
+ if (link.Selected)
+ yield return link;
- foreach (var group in Groups)
+ foreach (var vertex in link.Vertices)
{
- if (group.Selected)
- yield return group;
+ if (vertex.Selected)
+ yield return vertex;
}
}
- public void SelectModel(SelectableModel model, bool unselectOthers)
+ foreach (var group in Groups)
{
- if (model.Selected)
- return;
+ if (group.Selected)
+ yield return group;
+ }
+ }
- if (unselectOthers)
- UnselectAll();
+ public void SelectModel(SelectableModel model, bool unselectOthers)
+ {
+ if (model.Selected)
+ return;
- model.Selected = true;
- model.Refresh();
- SelectionChanged?.Invoke(model);
- }
+ if (unselectOthers)
+ UnselectAll();
- public void UnselectModel(SelectableModel model)
- {
- if (!model.Selected)
- return;
+ model.Selected = true;
+ model.Refresh();
+ SelectionChanged?.Invoke(model);
+ }
+
+ public void UnselectModel(SelectableModel model)
+ {
+ if (!model.Selected)
+ return;
+
+ model.Selected = false;
+ model.Refresh();
+ SelectionChanged?.Invoke(model);
+ }
+ public void UnselectAll()
+ {
+ foreach (var model in GetSelectedModels())
+ {
model.Selected = false;
model.Refresh();
+ // Todo: will result in many events, maybe one event for all of them?
SelectionChanged?.Invoke(model);
}
+ }
- public void UnselectAll()
- {
- foreach (var model in GetSelectedModels())
- {
- model.Selected = false;
- model.Refresh();
- // Todo: will result in many events, maybe one event for all of them?
- SelectionChanged?.Invoke(model);
- }
- }
-
- #endregion
+ #endregion
- #region Behaviors
+ #region Behaviors
- public void RegisterBehavior(Behavior behavior)
- {
- var type = behavior.GetType();
- if (_behaviors.ContainsKey(type))
- throw new Exception($"Behavior '{type.Name}' already registered");
+ public void RegisterBehavior(Behavior behavior)
+ {
+ var type = behavior.GetType();
+ if (_behaviors.ContainsKey(type))
+ throw new Exception($"Behavior '{type.Name}' already registered");
- _behaviors.Add(type, behavior);
- }
+ _behaviors.Add(type, behavior);
+ }
- public T? GetBehavior() where T : Behavior
- {
- var type = typeof(T);
- return (T?)(_behaviors.ContainsKey(type) ? _behaviors[type] : null);
- }
+ public T? GetBehavior() where T : Behavior
+ {
+ var type = typeof(T);
+ return (T?)(_behaviors.ContainsKey(type) ? _behaviors[type] : null);
+ }
- public void UnregisterBehavior() where T : Behavior
- {
- var type = typeof(T);
- if (!_behaviors.ContainsKey(type))
- return;
+ public void UnregisterBehavior() where T : Behavior
+ {
+ var type = typeof(T);
+ if (!_behaviors.ContainsKey(type))
+ return;
- _behaviors[type].Dispose();
- _behaviors.Remove(type);
- }
+ _behaviors[type].Dispose();
+ _behaviors.Remove(type);
+ }
- #endregion
+ #endregion
- public void ZoomToFit(double margin = 10)
- {
- if (Container == null || Nodes.Count == 0)
- return;
+ public void ZoomToFit(double margin = 10)
+ {
+ if (Container == null || Nodes.Count == 0)
+ return;
- var selectedNodes = Nodes.Where(s => s.Selected);
- var nodesToUse = selectedNodes.Any() ? selectedNodes : Nodes;
- 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 selectedNodes = Nodes.Where(s => s.Selected);
+ var nodesToUse = selectedNodes.Any() ? selectedNodes : Nodes;
+ 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;
- SuspendRefresh = true;
+ SuspendRefresh = true;
- var xf = Container.Width / width;
- var yf = Container.Height / height;
- SetZoom(Math.Min(xf, yf));
+ var xf = Container.Width / width;
+ var yf = Container.Height / height;
+ SetZoom(Math.Min(xf, yf));
- var nx = Container.Left + Pan.X + minX * Zoom;
- var ny = Container.Top + Pan.Y + minY * Zoom;
- UpdatePan(Container.Left - nx, Container.Top - ny);
+ var nx = Container.Left + Pan.X + minX * Zoom;
+ var ny = Container.Top + Pan.Y + minY * Zoom;
+ UpdatePan(Container.Left - nx, Container.Top - ny);
- SuspendRefresh = false;
- Refresh();
- }
+ SuspendRefresh = false;
+ Refresh();
+ }
- public void SetPan(double x, double y)
- {
- Pan = new Point(x, y);
- PanChanged?.Invoke();
- Refresh();
- }
+ public void SetPan(double x, double y)
+ {
+ Pan = new Point(x, y);
+ PanChanged?.Invoke();
+ Refresh();
+ }
- public void UpdatePan(double deltaX, double deltaY)
- {
- Pan = Pan.Add(deltaX, deltaY);
- PanChanged?.Invoke();
- Refresh();
- }
+ public void UpdatePan(double deltaX, double deltaY)
+ {
+ Pan = Pan.Add(deltaX, deltaY);
+ PanChanged?.Invoke();
+ Refresh();
+ }
- public void SetZoom(double newZoom)
- {
- if (newZoom <= 0)
- throw new ArgumentException($"{nameof(newZoom)} cannot be equal or lower than 0");
+ public void SetZoom(double newZoom)
+ {
+ if (newZoom <= 0)
+ throw new ArgumentException($"{nameof(newZoom)} cannot be equal or lower than 0");
- if (newZoom < Options.Zoom.Minimum)
- newZoom = Options.Zoom.Minimum;
+ if (newZoom < Options.Zoom.Minimum)
+ newZoom = Options.Zoom.Minimum;
- Zoom = newZoom;
- ZoomChanged?.Invoke();
- Refresh();
- }
+ Zoom = newZoom;
+ ZoomChanged?.Invoke();
+ Refresh();
+ }
- public void SetContainer(Rectangle newRect)
- {
- if (newRect.Equals(Container))
- return;
+ public void SetContainer(Rectangle newRect)
+ {
+ if (newRect.Equals(Container))
+ return;
- Container = newRect;
- ContainerChanged?.Invoke();
- Refresh();
- }
+ Container = newRect;
+ ContainerChanged?.Invoke();
+ Refresh();
+ }
- 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");
+ 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");
- return new Point((clientX - Container.Left - Pan.X) / Zoom, (clientY - Container.Top - Pan.Y) / Zoom);
- }
+ 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");
+ 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);
- }
+ return new Point(clientX - Container.Left, clientY - Container.Top);
+ }
- public Point GetScreenPoint(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");
+ public Point GetScreenPoint(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(Zoom * clientX + Container.Left + Pan.X, Zoom * clientY + Container.Top + Pan.Y);
- }
+ return new Point(Zoom * clientX + Container.Left + Pan.X, Zoom * clientY + Container.Top + Pan.Y);
+ }
- #region Ordering
+ #region Ordering
- public void SendToBack(SelectableModel model)
- {
- var minOrder = GetMinOrder();
- if (model.Order == minOrder)
- return;
+ public void SendToBack(SelectableModel model)
+ {
+ var minOrder = GetMinOrder();
+ if (model.Order == minOrder)
+ return;
- if (!_orderedSelectables.Remove(model))
- return;
+ if (!_orderedSelectables.Remove(model))
+ return;
- _orderedSelectables.Insert(0, model);
+ _orderedSelectables.Insert(0, model);
- // Todo: can optimize this by only updating the order of items before model
- Batch(() =>
+ // Todo: can optimize this by only updating the order of items before model
+ Batch(() =>
+ {
+ SuspendSorting = true;
+ for (var i = 0; i < _orderedSelectables.Count; i++)
{
- SuspendSorting = true;
- for (var i = 0; i < _orderedSelectables.Count; i++)
- {
- _orderedSelectables[i].Order = i + 1;
- }
- SuspendSorting = false;
- });
- }
+ _orderedSelectables[i].Order = i + 1;
+ }
+ SuspendSorting = false;
+ });
+ }
- public void SendToFront(SelectableModel model)
- {
- var maxOrder = GetMaxOrder();
- if (model.Order == maxOrder)
- return;
+ public void SendToFront(SelectableModel model)
+ {
+ var maxOrder = GetMaxOrder();
+ if (model.Order == maxOrder)
+ return;
- if (!_orderedSelectables.Remove(model))
- return;
+ if (!_orderedSelectables.Remove(model))
+ return;
- _orderedSelectables.Add(model);
+ _orderedSelectables.Add(model);
- SuspendSorting = true;
- model.Order = maxOrder + 1;
- SuspendSorting = false;
- Refresh();
- }
+ SuspendSorting = true;
+ model.Order = maxOrder + 1;
+ SuspendSorting = false;
+ Refresh();
+ }
- public int GetMinOrder()
- {
- return _orderedSelectables.Count > 0 ? _orderedSelectables[0].Order : 0;
- }
+ public int GetMinOrder()
+ {
+ return _orderedSelectables.Count > 0 ? _orderedSelectables[0].Order : 0;
+ }
- public int GetMaxOrder()
- {
- return _orderedSelectables.Count > 0 ? _orderedSelectables[^1].Order : 0;
- }
+ public int GetMaxOrder()
+ {
+ return _orderedSelectables.Count > 0 ? _orderedSelectables[^1].Order : 0;
+ }
- ///
- /// Sorts the list of selectables based on their order
- ///
- public void RefreshOrders(bool refresh = true)
+ ///
+ /// Sorts the list of selectables based on their order
+ ///
+ public void RefreshOrders(bool refresh = true)
+ {
+ _orderedSelectables.Sort((a, b) => a.Order.CompareTo(b.Order));
+
+ if (refresh)
{
- _orderedSelectables.Sort((a, b) => a.Order.CompareTo(b.Order));
-
- if (refresh)
- {
- Refresh();
- }
+ Refresh();
}
+ }
- private void OnSelectableAdded(SelectableModel model)
- {
- var maxOrder = GetMaxOrder();
- _orderedSelectables.Add(model);
-
- if (model.Order == 0)
- {
- model.Order = maxOrder + 1;
- }
-
- model.OrderChanged += OnModelOrderChanged;
- }
+ private void OnSelectableAdded(SelectableModel model)
+ {
+ var maxOrder = GetMaxOrder();
+ _orderedSelectables.Add(model);
- private void OnSelectableRemoved(SelectableModel model)
+ if (model.Order == 0)
{
- model.OrderChanged -= OnModelOrderChanged;
- _orderedSelectables.Remove(model);
+ model.Order = maxOrder + 1;
}
- private void OnModelOrderChanged(Model model)
- {
- if (SuspendSorting)
- return;
+ model.OrderChanged += OnModelOrderChanged;
+ }
- RefreshOrders();
- }
+ private void OnSelectableRemoved(SelectableModel model)
+ {
+ model.OrderChanged -= OnModelOrderChanged;
+ _orderedSelectables.Remove(model);
+ }
+
+ private void OnModelOrderChanged(Model model)
+ {
+ if (SuspendSorting)
+ return;
+
+ RefreshOrders();
+ }
- #endregion
+ #endregion
- #region Events
+ #region Events
- public void TriggerPointerDown(Model? model, PointerEventArgs e) => PointerDown?.Invoke(model, e);
+ public void TriggerPointerDown(Model? model, PointerEventArgs e) => PointerDown?.Invoke(model, e);
- public void TriggerPointerMove(Model? model, PointerEventArgs e) => PointerMove?.Invoke(model, e);
+ public void TriggerPointerMove(Model? model, PointerEventArgs e) => PointerMove?.Invoke(model, e);
- public void TriggerPointerUp(Model? model, PointerEventArgs e) => PointerUp?.Invoke(model, e);
+ public void TriggerPointerUp(Model? model, PointerEventArgs e) => PointerUp?.Invoke(model, e);
- public void TriggerPointerEnter(Model? model, PointerEventArgs e) => PointerEnter?.Invoke(model, e);
+ public void TriggerPointerEnter(Model? model, PointerEventArgs e) => PointerEnter?.Invoke(model, e);
- public void TriggerPointerLeave(Model? model, PointerEventArgs e) => PointerLeave?.Invoke(model, e);
+ public void TriggerPointerLeave(Model? model, PointerEventArgs e) => PointerLeave?.Invoke(model, e);
- public void TriggerKeyDown(KeyboardEventArgs e) => KeyDown?.Invoke(e);
+ public void TriggerKeyDown(KeyboardEventArgs e) => KeyDown?.Invoke(e);
- public void TriggerWheel(WheelEventArgs e) => Wheel?.Invoke(e);
+ public void TriggerWheel(WheelEventArgs e) => Wheel?.Invoke(e);
- public void TriggerPointerClick(Model? model, PointerEventArgs e) => PointerClick?.Invoke(model, e);
+ public void TriggerPointerClick(Model? model, PointerEventArgs e) => PointerClick?.Invoke(model, e);
- public void TriggerPointerDoubleClick(Model? model, PointerEventArgs e) => PointerDoubleClick?.Invoke(model, e);
+ public void TriggerPointerDoubleClick(Model? model, PointerEventArgs e) => PointerDoubleClick?.Invoke(model, e);
- #endregion
- }
+ #endregion
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams.Core/DiagramsException.cs b/src/Blazor.Diagrams.Core/DiagramsException.cs
index ac16ec75b..f56cdc0c4 100644
--- a/src/Blazor.Diagrams.Core/DiagramsException.cs
+++ b/src/Blazor.Diagrams.Core/DiagramsException.cs
@@ -1,11 +1,10 @@
using System;
-namespace Blazor.Diagrams.Core
+namespace Blazor.Diagrams.Core;
+
+public class DiagramsException : Exception
{
- public class DiagramsException : Exception
+ public DiagramsException(string? message) : base(message)
{
- public DiagramsException(string? message) : base(message)
- {
- }
}
}
diff --git a/src/Blazor.Diagrams.Core/Events/KeyboardEventArgs.cs b/src/Blazor.Diagrams.Core/Events/KeyboardEventArgs.cs
index b5791ab5d..ee1fc02cd 100644
--- a/src/Blazor.Diagrams.Core/Events/KeyboardEventArgs.cs
+++ b/src/Blazor.Diagrams.Core/Events/KeyboardEventArgs.cs
@@ -1,4 +1,3 @@
-namespace Blazor.Diagrams.Core.Events
-{
- public record KeyboardEventArgs(string Key, string Code, float Location, bool CtrlKey, bool ShiftKey, bool AltKey);
-}
+namespace Blazor.Diagrams.Core.Events;
+
+public record KeyboardEventArgs(string Key, string Code, float Location, bool CtrlKey, bool ShiftKey, bool AltKey);
diff --git a/src/Blazor.Diagrams.Core/Events/MouseEventArgs.cs b/src/Blazor.Diagrams.Core/Events/MouseEventArgs.cs
index e083db90b..94c4cba58 100644
--- a/src/Blazor.Diagrams.Core/Events/MouseEventArgs.cs
+++ b/src/Blazor.Diagrams.Core/Events/MouseEventArgs.cs
@@ -1,4 +1,3 @@
-namespace Blazor.Diagrams.Core.Events
-{
- public record MouseEventArgs(double ClientX, double ClientY, long Button, long Buttons, bool CtrlKey, bool ShiftKey, bool AltKey);
-}
+namespace Blazor.Diagrams.Core.Events;
+
+public record MouseEventArgs(double ClientX, double ClientY, long Button, long Buttons, bool CtrlKey, bool ShiftKey, bool AltKey);
diff --git a/src/Blazor.Diagrams.Core/Events/TouchEventArgs.cs b/src/Blazor.Diagrams.Core/Events/TouchEventArgs.cs
index 9c20defe7..231234390 100644
--- a/src/Blazor.Diagrams.Core/Events/TouchEventArgs.cs
+++ b/src/Blazor.Diagrams.Core/Events/TouchEventArgs.cs
@@ -1,5 +1,4 @@
-namespace Blazor.Diagrams.Core.Events
-{
- public record TouchEventArgs(TouchPoint[] ChangedTouches, bool CtrlKey, bool ShiftKey, bool AltKey);
- public record TouchPoint(long Identifier, double ClientX, double ClientY);
-}
+namespace Blazor.Diagrams.Core.Events;
+
+public record TouchEventArgs(TouchPoint[] ChangedTouches, bool CtrlKey, bool ShiftKey, bool AltKey);
+public record TouchPoint(long Identifier, double ClientX, double ClientY);
diff --git a/src/Blazor.Diagrams.Core/Events/WheelEventArgs.cs b/src/Blazor.Diagrams.Core/Events/WheelEventArgs.cs
index d51b0687d..826b61e55 100644
--- a/src/Blazor.Diagrams.Core/Events/WheelEventArgs.cs
+++ b/src/Blazor.Diagrams.Core/Events/WheelEventArgs.cs
@@ -1,15 +1,14 @@
-namespace Blazor.Diagrams.Core.Events
-{
- public record WheelEventArgs(
- double ClientX,
- double ClientY,
- long Button,
- long Buttons,
- bool CtrlKey,
- bool ShiftKey,
- bool AltKey,
- double DeltaX,
- double DeltaY,
- double DeltaZ,
- long DeltaMode) : MouseEventArgs(ClientX, ClientY, Button, Buttons, CtrlKey, ShiftKey, AltKey);
-}
+namespace Blazor.Diagrams.Core.Events;
+
+public record WheelEventArgs(
+ double ClientX,
+ double ClientY,
+ long Button,
+ long Buttons,
+ bool CtrlKey,
+ bool ShiftKey,
+ bool AltKey,
+ double DeltaX,
+ double DeltaY,
+ double DeltaZ,
+ long DeltaMode) : MouseEventArgs(ClientX, ClientY, Button, Buttons, CtrlKey, ShiftKey, AltKey);
diff --git a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs
index 2d45f2c06..24d8572aa 100644
--- a/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs
+++ b/src/Blazor.Diagrams.Core/Extensions/DiagramExtensions.cs
@@ -3,47 +3,46 @@
using System.Collections.Generic;
using System.Linq;
-namespace Blazor.Diagrams.Core.Extensions
+namespace Blazor.Diagrams.Core.Extensions;
+
+public static class DiagramExtensions
{
- public static class DiagramExtensions
+ public static Rectangle GetBounds(this IEnumerable nodes)
{
- public static Rectangle GetBounds(this IEnumerable nodes)
- {
- if (!nodes.Any())
- return Rectangle.Zero;
+ if (!nodes.Any())
+ return Rectangle.Zero;
- var minX = double.MaxValue;
- var maxX = double.MinValue;
- var minY = double.MaxValue;
- var maxY = double.MinValue;
+ var minX = double.MaxValue;
+ var maxX = double.MinValue;
+ var minY = double.MaxValue;
+ var maxY = double.MinValue;
- foreach (var node in nodes)
- {
- if (node.Size == null) // Ignore nodes that didn't get a size yet
- continue;
+ foreach (var node in nodes)
+ {
+ if (node.Size == null) // Ignore nodes that didn't get a size yet
+ continue;
- var trX = node.Position.X + node.Size!.Width;
- var bY = node.Position.Y + node.Size.Height;
+ 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;
- }
+ 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 new Rectangle(minX, minY, maxX, maxY);
}
+
+ return new Rectangle(minX, minY, maxX, maxY);
}
}
diff --git a/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs
index 9f3832bad..1b3d9ed56 100644
--- a/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs
+++ b/src/Blazor.Diagrams.Core/Extensions/DoubleExtensions.cs
@@ -1,10 +1,9 @@
using System;
-namespace Blazor.Diagrams.Core.Extensions
+namespace Blazor.Diagrams.Core.Extensions;
+
+public static class DoubleExtensions
{
- public static class DoubleExtensions
- {
- public static bool AlmostEqualTo(this double double1, double double2, double tolerance = 0.0001)
- => Math.Abs(double1 - double2) < tolerance;
- }
+ 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/Extensions/NumberExtensions.cs b/src/Blazor.Diagrams.Core/Extensions/NumberExtensions.cs
index b5aa06f7b..e4c049991 100644
--- a/src/Blazor.Diagrams.Core/Extensions/NumberExtensions.cs
+++ b/src/Blazor.Diagrams.Core/Extensions/NumberExtensions.cs
@@ -1,9 +1,8 @@
using System.Globalization;
-namespace Blazor.Diagrams.Core.Extensions
+namespace Blazor.Diagrams.Core.Extensions;
+
+public static class NumberExtensions
{
- public static class NumberExtensions
- {
- public static string ToInvariantString(this double n) => n.ToString(CultureInfo.InvariantCulture);
- }
+ public static string ToInvariantString(this double n) => n.ToString(CultureInfo.InvariantCulture);
}
diff --git a/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs b/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs
index 4c3a8faaf..860d89ae9 100644
--- a/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/BezierSpline.cs
@@ -1,206 +1,205 @@
using System;
-namespace Blazor.Diagrams.Core.Geometry
+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
{
///
- /// Bezier Spline methods
+ /// Get open-ended Bezier Spline Control Points.
///
- ///
- /// 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
+ /// 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)
{
- ///
- /// 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);
+ 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];
+ 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;
+ }
- // 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);
+ // Calculate first Bezier control points
+ // Right hand side vector
+ double[] rhs = new double[n];
- // 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);
+ // 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);
- // 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);
- }
- }
+ // 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);
- ///
- /// 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)
+ // Fill output arrays.
+ firstControlPoints = new Point[n];
+ secondControlPoints = new Point[n];
+ for (int i = 0; i < n; ++i)
{
- int n = rhs.Length;
- double[] x = new double[n]; // Solution vector.
- double[] tmp = new double[n]; // Temp workspace.
+ // 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);
+ }
+ }
- 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.
+ ///
+ /// 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.
- return x;
+ 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/Geometry/Ellipse.cs b/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs
index e64896b6d..771a08182 100644
--- a/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/Ellipse.cs
@@ -1,68 +1,67 @@
using System;
using System.Collections.Generic;
-namespace Blazor.Diagrams.Core.Geometry
+namespace Blazor.Diagrams.Core.Geometry;
+
+public class Ellipse : IShape
{
- public class Ellipse : IShape
+ public Ellipse(double cx, double cy, double rx, double ry)
{
- public Ellipse(double cx, double cy, double rx, double ry)
- {
- Cx = cx;
- Cy = cy;
- Rx = rx;
- Ry = ry;
- }
+ Cx = cx;
+ Cy = cy;
+ Rx = rx;
+ Ry = ry;
+ }
- public double Cx { get; }
- public double Cy { get; }
- public double Rx { get; }
- public double Ry { get; }
+ public double Cx { get; }
+ public double Cy { 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(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));
+ 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(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));
- var a = dir.Dot(mDir);
- var b = dir.Dot(mDiff);
- var c = diff.Dot(mDiff) - 1.0;
- var d = b * b - a * c;
+ 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 (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 (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);
- }
+ if (0 <= tb && tb <= 1)
+ yield return a1.Lerp(a2, tb);
}
- else
+ }
+ else
+ {
+ var t = -b / a;
+ if (0 <= t && t <= 1)
{
- var t = -b / a;
- if (0 <= t && t <= 1)
- {
- yield return a1.Lerp(a2, t);
- }
+ yield return a1.Lerp(a2, t);
}
}
+ }
- public Point? GetPointAtAngle(double a)
- {
- var t = Math.Tan(a / 360 * Math.PI);
- var px = Rx * (1 - Math.Pow(t, 2)) / (1 + Math.Pow(t, 2));
- var py = Ry * 2 * t / (1 + Math.Pow(t, 2));
- return new Point(Cx + px, Cy + py);
- }
+ public Point? GetPointAtAngle(double a)
+ {
+ var t = Math.Tan(a / 360 * Math.PI);
+ var px = Rx * (1 - Math.Pow(t, 2)) / (1 + Math.Pow(t, 2));
+ var py = Ry * 2 * t / (1 + Math.Pow(t, 2));
+ return new Point(Cx + px, Cy + py);
}
}
diff --git a/src/Blazor.Diagrams.Core/Geometry/IShape.cs b/src/Blazor.Diagrams.Core/Geometry/IShape.cs
index b5402d981..05b47130f 100644
--- a/src/Blazor.Diagrams.Core/Geometry/IShape.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/IShape.cs
@@ -1,10 +1,9 @@
using System.Collections.Generic;
-namespace Blazor.Diagrams.Core.Geometry
+namespace Blazor.Diagrams.Core.Geometry;
+
+public interface IShape
{
- public interface IShape
- {
- public IEnumerable GetIntersectionsWithLine(Line line);
- public Point? GetPointAtAngle(double a);
- }
+ public IEnumerable GetIntersectionsWithLine(Line line);
+ public Point? GetPointAtAngle(double a);
}
diff --git a/src/Blazor.Diagrams.Core/Geometry/Line.cs b/src/Blazor.Diagrams.Core/Geometry/Line.cs
index d5fca90e9..45add1cd1 100644
--- a/src/Blazor.Diagrams.Core/Geometry/Line.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/Line.cs
@@ -1,43 +1,42 @@
-namespace Blazor.Diagrams.Core.Geometry
+namespace Blazor.Diagrams.Core.Geometry;
+
+public class Line
{
- public class Line
+ public Line(Point start, Point end)
{
- public Line(Point start, Point end)
- {
- Start = start;
- End = end;
- }
+ Start = start;
+ End = end;
+ }
- public Point Start { get; }
- public Point End { get; }
+ 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;
+ 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)
- {
- if (alpha > det || beta > det)
- return null;
+ if (det == 0 || alpha * det < 0 || beta * det < 0)
+ return null;
- }
- else
- {
- if (alpha < det || beta < det)
- return null;
- }
+ if (det > 0)
+ {
+ if (alpha > det || beta > det)
+ return null;
- return new Point(Start.X + (alpha * pt1Dir.X / det), Start.Y + (alpha * pt1Dir.Y / det));
+ }
+ else
+ {
+ if (alpha < det || beta < det)
+ return null;
}
- public override string ToString() => $"Line from {Start} to {End}";
+ 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/Geometry/Point.cs b/src/Blazor.Diagrams.Core/Geometry/Point.cs
index 088048531..272323d94 100644
--- a/src/Blazor.Diagrams.Core/Geometry/Point.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/Point.cs
@@ -1,52 +1,51 @@
using System;
using System.Reflection.Metadata;
-namespace Blazor.Diagrams.Core.Geometry
+namespace Blazor.Diagrams.Core.Geometry;
+
+public record Point
{
- public record Point
+ public static Point Zero { get; } = new(0, 0);
+
+ public Point(double x, double y)
+ {
+ X = x;
+ Y = y;
+ }
+
+ public double X { get; init; }
+ public double Y { get; init; }
+
+ public double Dot(Point other) => X * other.X + Y * other.Y;
+ public Point Lerp(Point other, double t)
+ => new(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(X + value, Y + value);
+ public Point Add(double x, double y) => new(X + x, Y + y);
+
+ public Point Substract(double value) => new(X - value, Y - value);
+ public Point Substract(double x, double y) => new(X - x, Y - y);
+
+ public double DistanceTo(Point other) => Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
+ public double DistanceTo(double x, double y) => Math.Sqrt(Math.Pow(X - x, 2) + Math.Pow(Y - y, 2));
+
+ public Point MoveAlongLine(Point from, double dist)
+ {
+ var x = X - from.X;
+ var y = Y - from.Y;
+ var angle = Math.Atan2(y, x);
+ var xOffset = Math.Cos(angle) * dist;
+ var yOffset = Math.Sin(angle) * dist;
+ return new Point(X + xOffset, Y + yOffset);
+ }
+
+ public static Point operator -(Point a, Point b) => new(a.X - b.X, a.Y - b.Y);
+ public static Point operator +(Point a, Point b) => new(a.X + b.X, a.Y + b.Y);
+
+ public void Deconstruct(out double x, out double y)
{
- public static Point Zero { get; } = new(0, 0);
-
- public Point(double x, double y)
- {
- X = x;
- Y = y;
- }
-
- public double X { get; init; }
- public double Y { get; init; }
-
- public double Dot(Point other) => X * other.X + Y * other.Y;
- public Point Lerp(Point other, double t)
- => new(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(X + value, Y + value);
- public Point Add(double x, double y) => new(X + x, Y + y);
-
- public Point Substract(double value) => new(X - value, Y - value);
- public Point Substract(double x, double y) => new(X - x, Y - y);
-
- public double DistanceTo(Point other) => Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
- public double DistanceTo(double x, double y) => Math.Sqrt(Math.Pow(X - x, 2) + Math.Pow(Y - y, 2));
-
- public Point MoveAlongLine(Point from, double dist)
- {
- var x = X - from.X;
- var y = Y - from.Y;
- var angle = Math.Atan2(y, x);
- var xOffset = Math.Cos(angle) * dist;
- var yOffset = Math.Sin(angle) * dist;
- return new Point(X + xOffset, Y + yOffset);
- }
-
- public static Point operator -(Point a, Point b) => new(a.X - b.X, a.Y - b.Y);
- public static Point operator +(Point a, Point b) => new(a.X + b.X, a.Y + b.Y);
-
- public void Deconstruct(out double x, out double y)
- {
- x = X;
- y = Y;
- }
+ x = X;
+ y = Y;
}
}
diff --git a/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs b/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs
index dbea08cfa..c14309d71 100644
--- a/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/Rectangle.cs
@@ -3,128 +3,127 @@
using System.Linq;
using System.Text.Json.Serialization;
-namespace Blazor.Diagrams.Core.Geometry
+namespace Blazor.Diagrams.Core.Geometry;
+
+public class Rectangle : IShape
{
- public class Rectangle : IShape
- {
- public static Rectangle Zero { get; } = new(0, 0, 0, 0);
+ public static Rectangle Zero { get; } = new(0, 0, 0, 0);
- public double Width { get; }
- public double Height { get; }
- public double Top { get; }
- public double Right { get; }
- public double Bottom { get; }
- public double Left { get; }
+ public double Width { get; }
+ public double Height { get; }
+ public double Top { get; }
+ public double Right { get; }
+ public double Bottom { get; }
+ public double Left { get; }
- [JsonConstructor]
- 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);
- }
+ [JsonConstructor]
+ 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)
- {
- ArgumentNullException.ThrowIfNull(position, nameof(position));
- ArgumentNullException.ThrowIfNull(size, nameof(size));
-
- Left = position.X;
- Top = position.Y;
- Right = Left + size.Width;
- Bottom = Top + size.Height;
- Width = size.Width;
- Height = size.Height;
- }
+ public Rectangle(Point position, Size size)
+ {
+ ArgumentNullException.ThrowIfNull(position, nameof(position));
+ ArgumentNullException.ThrowIfNull(size, nameof(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 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 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(Left - horizontal, Top - vertical, Right + horizontal, Bottom + vertical);
+ public Rectangle Inflate(double horizontal, double vertical)
+ => new(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(x1, y1, x2, y2);
- }
+ 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(x1, y1, x2, y2);
+ }
- public bool ContainsPoint(Point point) => ContainsPoint(point.X, point.Y);
+ 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 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? GetPointAtAngle(double a)
+ 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 vx = Math.Cos(a * Math.PI / 180);
- var vy = Math.Sin(a * Math.PI / 180);
- var px = Left + Width / 2;
- var py = Top + Height / 2;
- double? t1 = (Left - px) / vx; // left
- double? t2 = (Right - px) / vx; // right
- double? t3 = (Top - py) / vy; // top
- double? t4 = (Bottom - py) / vy; // bottom
- var t = (new[] { t1, t2, t3, t4 }).Where(n => n.HasValue && double.IsFinite(n.Value) && n.Value > 0).DefaultIfEmpty(null).Min();
- if (t == null) return null;
-
- var x = px + t.Value * vx;
- var y = py + t.Value * vy;
- return new Point(x, y);
+ var intersectionPt = borders[i].GetIntersection(line);
+ if (intersectionPt != null)
+ yield return intersectionPt;
}
+ }
- public Point Center => new(Left + Width / 2, Top + Height / 2);
- public Point NorthEast => new(Right, Top);
- public Point SouthEast => new(Right, Bottom);
- public Point SouthWest => new(Left, Bottom);
- public Point NorthWest => new(Left, Top);
- public Point East => new(Right, Top + Height / 2);
- public Point North => new(Left + Width / 2, Top);
- public Point South => new(Left + Width / 2, Bottom);
- public Point West => new(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 Point? GetPointAtAngle(double a)
+ {
+ var vx = Math.Cos(a * Math.PI / 180);
+ var vy = Math.Sin(a * Math.PI / 180);
+ var px = Left + Width / 2;
+ var py = Top + Height / 2;
+ double? t1 = (Left - px) / vx; // left
+ double? t2 = (Right - px) / vx; // right
+ double? t3 = (Top - py) / vy; // top
+ double? t4 = (Bottom - py) / vy; // bottom
+ var t = (new[] { t1, t2, t3, t4 }).Where(n => n.HasValue && double.IsFinite(n.Value) && n.Value > 0).DefaultIfEmpty(null).Min();
+ if (t == null) return null;
+
+ var x = px + t.Value * vx;
+ var y = py + t.Value * vy;
+ return new Point(x, y);
+ }
- public override string ToString()
- => $"Rectangle(width={Width}, height={Height}, top={Top}, right={Right}, bottom={Bottom}, left={Left})";
+ public Point Center => new(Left + Width / 2, Top + Height / 2);
+ public Point NorthEast => new(Right, Top);
+ public Point SouthEast => new(Right, Bottom);
+ public Point SouthWest => new(Left, Bottom);
+ public Point NorthWest => new(Left, Top);
+ public Point East => new(Right, Top + Height / 2);
+ public Point North => new(Left + Width / 2, Top);
+ public Point South => new(Left + Width / 2, Bottom);
+ public Point West => new(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})";
}
diff --git a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs
index 75fb58487..9bb9f2e05 100644
--- a/src/Blazor.Diagrams.Core/Geometry/Shapes.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/Shapes.cs
@@ -1,38 +1,37 @@
using Blazor.Diagrams.Core.Models;
-namespace Blazor.Diagrams.Core.Geometry
+namespace Blazor.Diagrams.Core.Geometry;
+
+public static class Shapes
{
- public static class Shapes
- {
- public static IShape Rectangle(NodeModel node) => Rectangle(node.Position, node.Size!);
+ public static IShape Rectangle(NodeModel node) => Rectangle(node.Position, node.Size!);
- public static IShape Circle(NodeModel node) => Circle(node.Position, node.Size!);
+ public static IShape Circle(NodeModel node) => Circle(node.Position, node.Size!);
- public static IShape Ellipse(NodeModel node) => Ellipse(node.Position, node.Size!);
+ public static IShape Ellipse(NodeModel node) => Ellipse(node.Position, node.Size!);
- public static IShape Rectangle(PortModel port) => Rectangle(port.Position, port.Size!);
+ public static IShape Rectangle(PortModel port) => Rectangle(port.Position, port.Size!);
- public static IShape Circle(PortModel port) => Circle(port.Position, port.Size!);
+ public static IShape Circle(PortModel port) => Circle(port.Position, port.Size!);
- public static IShape Ellipse(PortModel port) => Ellipse(port.Position, port.Size!);
-
- private static IShape Rectangle(Point position, Size size) => new Rectangle(position, size);
+ public static IShape Ellipse(PortModel port) => Ellipse(port.Position, port.Size!);
+
+ private static IShape Rectangle(Point position, Size size) => new Rectangle(position, size);
- private static IShape Circle(Point position, Size size)
- {
- var halfWidth = size.Width / 2;
- var centerX = position.X + halfWidth;
- var centerY = position.Y + size.Height / 2;
- return new Ellipse(centerX, centerY, halfWidth, halfWidth);
- }
+ private static IShape Circle(Point position, Size size)
+ {
+ var halfWidth = size.Width / 2;
+ var centerX = position.X + halfWidth;
+ var centerY = position.Y + size.Height / 2;
+ return new Ellipse(centerX, centerY, halfWidth, halfWidth);
+ }
- private static IShape Ellipse(Point position, Size size)
- {
- var halfWidth = size.Width / 2;
- var halfHeight = size.Height / 2;
- var centerX = position.X + halfWidth;
- var centerY = position.Y + halfHeight;
- return new Ellipse(centerX, centerY, halfWidth, halfHeight);
- }
+ private static IShape Ellipse(Point position, Size size)
+ {
+ var halfWidth = size.Width / 2;
+ var halfHeight = size.Height / 2;
+ var centerX = position.X + halfWidth;
+ var centerY = position.Y + halfHeight;
+ return new Ellipse(centerX, centerY, halfWidth, halfHeight);
}
}
diff --git a/src/Blazor.Diagrams.Core/Geometry/Size.cs b/src/Blazor.Diagrams.Core/Geometry/Size.cs
index 4207884e8..97cb89ead 100644
--- a/src/Blazor.Diagrams.Core/Geometry/Size.cs
+++ b/src/Blazor.Diagrams.Core/Geometry/Size.cs
@@ -1,16 +1,15 @@
-namespace Blazor.Diagrams.Core.Geometry
-{
- public record Size
- {
- public static Size Zero { get; } = new(0, 0);
+namespace Blazor.Diagrams.Core.Geometry;
- public Size(double width, double height)
- {
- Width = width;
- Height = height;
- }
+public record Size
+{
+ public static Size Zero { get; } = new(0, 0);
- public double Width { get; init; }
- public double Height { get; init; }
+ public Size(double width, double height)
+ {
+ Width = width;
+ Height = height;
}
+
+ public double Width { get; init; }
+ public double Height { get; init; }
}
diff --git a/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs b/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs
index 57d21bc69..711e19727 100644
--- a/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs
+++ b/src/Blazor.Diagrams.Core/Layers/BaseLayer.cs
@@ -3,111 +3,110 @@
using System.Collections;
using System.Collections.Generic;
-namespace Blazor.Diagrams.Core
+namespace Blazor.Diagrams.Core;
+
+public abstract class BaseLayer : IReadOnlyList where T : Model
{
- 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)
{
- private readonly List _items = new List();
+ Diagram = diagram;
+ }
- public event Action? Added;
- public event Action? Removed;
+ public virtual TSpecific Add(TSpecific item) where TSpecific : T
+ {
+ if (item is null)
+ throw new ArgumentNullException(nameof(item));
- public BaseLayer(Diagram diagram)
+ Diagram.Batch(() =>
{
- Diagram = diagram;
- }
+ _items.Add(item);
+ OnItemAdded(item);
+ Added?.Invoke(item);
+ });
+ return item;
+ }
- public virtual TSpecific Add(TSpecific item) where TSpecific : T
- {
- if (item is null)
- throw new ArgumentNullException(nameof(item));
+ public virtual void Add(IEnumerable items)
+ {
+ if (items is null)
+ throw new ArgumentNullException(nameof(items));
- Diagram.Batch(() =>
+ Diagram.Batch(() =>
+ {
+ foreach (var item in items)
{
_items.Add(item);
OnItemAdded(item);
Added?.Invoke(item);
- });
- return item;
- }
+ }
+ });
+ }
- public virtual void Add(IEnumerable items)
- {
- if (items is null)
- throw new ArgumentNullException(nameof(items));
+ public virtual void Remove(T item)
+ {
+ if (item is null)
+ throw new ArgumentNullException(nameof(item));
+ if (_items.Remove(item))
+ {
Diagram.Batch(() =>
{
- foreach (var item in items)
- {
- _items.Add(item);
- OnItemAdded(item);
- Added?.Invoke(item);
- }
+ OnItemRemoved(item);
+ Removed?.Invoke(item);
});
}
+ }
- public virtual void Remove(T item)
- {
- if (item is null)
- throw new ArgumentNullException(nameof(item));
+ public virtual void Remove(IEnumerable items)
+ {
+ if (items is null)
+ throw new ArgumentNullException(nameof(items));
- if (_items.Remove(item))
+ Diagram.Batch(() =>
+ {
+ foreach (var item in items)
{
- Diagram.Batch(() =>
+ if (_items.Remove(item))
{
OnItemRemoved(item);
Removed?.Invoke(item);
- });
+ }
}
- }
-
- 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 bool Contains(T item) => _items.Contains(item);
+ public void Clear()
+ {
+ if (Count == 0)
+ return;
- public void Clear()
+ Diagram.Batch(() =>
{
- if (Count == 0)
- return;
-
- Diagram.Batch(() =>
+ for (var i = _items.Count - 1; i >= 0; i--)
{
- for (var i = _items.Count - 1; i >= 0; i--)
- {
- var item = _items[i];
- _items.RemoveAt(i);
- OnItemRemoved(item);
- Removed?.Invoke(item);
- }
- });
- }
+ var item = _items[i];
+ _items.RemoveAt(i);
+ OnItemRemoved(item);
+ Removed?.Invoke(item);
+ }
+ });
+ }
- protected virtual void OnItemAdded(T item) { }
+ protected virtual void OnItemAdded(T item) { }
- protected virtual void OnItemRemoved(T item) { }
+ protected virtual void OnItemRemoved(T item) { }
- public Diagram Diagram { get; }
+ 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();
- }
+ 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/GroupLayer.cs b/src/Blazor.Diagrams.Core/Layers/GroupLayer.cs
index 31c89a9f5..9bb3b1779 100644
--- a/src/Blazor.Diagrams.Core/Layers/GroupLayer.cs
+++ b/src/Blazor.Diagrams.Core/Layers/GroupLayer.cs
@@ -1,50 +1,49 @@
using Blazor.Diagrams.Core.Models;
using System.Linq;
-namespace Blazor.Diagrams.Core.Layers
+namespace Blazor.Diagrams.Core.Layers;
+
+public class GroupLayer : BaseLayer
{
- public class GroupLayer : BaseLayer
+ public GroupLayer(Diagram diagram) : base(diagram)
{
- public GroupLayer(Diagram diagram) : base(diagram)
- {
- }
+ }
- public GroupModel Group(params NodeModel[] children)
- {
- return Add(Diagram.Options.Groups.Factory(Diagram, children));
- }
+ public GroupModel Group(params NodeModel[] children)
+ {
+ return Add(Diagram.Options.Groups.Factory(Diagram, children));
+ }
- ///
- /// Removes the group AND its children
- ///
- public void Delete(GroupModel group)
+ ///
+ /// Removes the group AND its children
+ ///
+ public void Delete(GroupModel group)
+ {
+ Diagram.Batch(() =>
{
- Diagram.Batch(() =>
- {
- var children = group.Children.ToArray();
+ var children = group.Children.ToArray();
- Remove(group);
+ Remove(group);
- foreach (var child in children)
+ foreach (var child in children)
+ {
+ if (child is GroupModel g)
+ {
+ Delete(g);
+ }
+ else
{
- if (child is GroupModel g)
- {
- Delete(g);
- }
- else
- {
- Diagram.Nodes.Remove(child);
- }
+ Diagram.Nodes.Remove(child);
}
- });
- }
+ }
+ });
+ }
- protected override void OnItemRemoved(GroupModel group)
- {
- Diagram.Links.Remove(group.PortLinks.ToArray());
- Diagram.Links.Remove(group.Links.ToArray());
- group.Ungroup();
- group.Group?.RemoveChild(group);
- }
+ protected override void OnItemRemoved(GroupModel group)
+ {
+ Diagram.Links.Remove(group.PortLinks.ToArray());
+ Diagram.Links.Remove(group.Links.ToArray());
+ group.Ungroup();
+ group.Group?.RemoveChild(group);
}
}
diff --git a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs
index 8d09f786b..b1e8983e4 100644
--- a/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs
+++ b/src/Blazor.Diagrams.Core/Layers/LinkLayer.cs
@@ -2,64 +2,63 @@
using Blazor.Diagrams.Core.Models.Base;
using System.Linq;
-namespace Blazor.Diagrams.Core.Layers
+namespace Blazor.Diagrams.Core.Layers;
+
+public class LinkLayer : BaseLayer
{
- public class LinkLayer : BaseLayer
+ public LinkLayer(Diagram diagram) : base(diagram) { }
+
+ protected override void OnItemAdded(BaseLinkModel link)
{
- public LinkLayer(Diagram diagram) : base(diagram) { }
+ link.Diagram = Diagram;
+ HandleAnchor(link, link.Source, true);
+ HandleAnchor(link, link.Target, true);
+ link.Refresh();
- protected override void OnItemAdded(BaseLinkModel link)
- {
- link.Diagram = Diagram;
- HandleAnchor(link, link.Source, true);
- HandleAnchor(link, link.Target, true);
- link.Refresh();
+ link.SourceChanged += OnLinkSourceChanged;
+ link.TargetChanged += OnLinkTargetChanged;
+ }
- link.SourceChanged += OnLinkSourceChanged;
- link.TargetChanged += OnLinkTargetChanged;
- }
+ protected override void OnItemRemoved(BaseLinkModel link)
+ {
+ link.Diagram = null;
+ HandleAnchor(link, link.Source, false);
+ HandleAnchor(link, link.Target, false);
+ link.Refresh();
- protected override void OnItemRemoved(BaseLinkModel link)
- {
- link.Diagram = null;
- HandleAnchor(link, link.Source, false);
- HandleAnchor(link, link.Target, false);
- link.Refresh();
+ link.SourceChanged -= OnLinkSourceChanged;
+ link.TargetChanged -= OnLinkTargetChanged;
+
+ Diagram.Controls.RemoveFor(link);
+ Remove(link.Links.ToList());
+ }
- link.SourceChanged -= OnLinkSourceChanged;
- link.TargetChanged -= OnLinkTargetChanged;
-
- Diagram.Controls.RemoveFor(link);
- Remove(link.Links.ToList());
- }
+ private static void OnLinkSourceChanged(BaseLinkModel link, Anchor old, Anchor @new)
+ {
+ HandleAnchor(link, old, add: false);
+ HandleAnchor(link, @new, add: true);
+ }
+
+ private static void OnLinkTargetChanged(BaseLinkModel link, Anchor old, Anchor @new)
+ {
+ HandleAnchor(link, old, add: false);
+ HandleAnchor(link, @new, add: true);
+ }
- private static void OnLinkSourceChanged(BaseLinkModel link, Anchor old, Anchor @new)
+ private static void HandleAnchor(BaseLinkModel link, Anchor anchor, bool add)
+ {
+ if (add)
{
- HandleAnchor(link, old, add: false);
- HandleAnchor(link, @new, add: true);
+ anchor.Model?.AddLink(link);
}
-
- private static void OnLinkTargetChanged(BaseLinkModel link, Anchor old, Anchor @new)
+ else
{
- HandleAnchor(link, old, add: false);
- HandleAnchor(link, @new, add: true);
+ anchor.Model?.RemoveLink(link);
}
- private static void HandleAnchor(BaseLinkModel link, Anchor anchor, bool add)
+ if (anchor.Model is Model model)
{
- if (add)
- {
- anchor.Model?.AddLink(link);
- }
- else
- {
- anchor.Model?.RemoveLink(link);
- }
-
- if (anchor.Model is Model model)
- {
- model.Refresh();
- }
+ model.Refresh();
}
}
}
diff --git a/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs b/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs
index 4cdd7b1ca..7a627d6a2 100644
--- a/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs
+++ b/src/Blazor.Diagrams.Core/Layers/NodeLayer.cs
@@ -1,18 +1,17 @@
using Blazor.Diagrams.Core.Models;
using System.Linq;
-namespace Blazor.Diagrams.Core.Layers
+namespace Blazor.Diagrams.Core.Layers;
+
+public class NodeLayer : BaseLayer
{
- public class NodeLayer : BaseLayer
- {
- public NodeLayer(Diagram diagram) : base(diagram) { }
+ public NodeLayer(Diagram diagram) : base(diagram) { }
- protected override void OnItemRemoved(NodeModel node)
- {
- Diagram.Links.Remove(node.PortLinks.ToList());
- Diagram.Links.Remove(node.Links.ToList());
- node.Group?.RemoveChild(node);
- Diagram.Controls.RemoveFor(node);
- }
+ protected override void OnItemRemoved(NodeModel node)
+ {
+ Diagram.Links.Remove(node.PortLinks.ToList());
+ Diagram.Links.Remove(node.Links.ToList());
+ node.Group?.RemoveChild(node);
+ Diagram.Controls.RemoveFor(node);
}
}
diff --git a/src/Blazor.Diagrams.Core/Models/Base/Model.cs b/src/Blazor.Diagrams.Core/Models/Base/Model.cs
index 8d277307f..738b45091 100644
--- a/src/Blazor.Diagrams.Core/Models/Base/Model.cs
+++ b/src/Blazor.Diagrams.Core/Models/Base/Model.cs
@@ -1,36 +1,35 @@
using System;
-namespace Blazor.Diagrams.Core.Models.Base
+namespace Blazor.Diagrams.Core.Models.Base;
+
+public abstract class Model
{
- public abstract class Model
- {
- private bool _visible = true;
-
- protected Model() : this(Guid.NewGuid().ToString()) { }
+ private bool _visible = true;
+
+ protected Model() : this(Guid.NewGuid().ToString()) { }
- protected Model(string id)
- {
- Id = id;
- }
+ protected Model(string id)
+ {
+ Id = id;
+ }
- public event Action? Changed;
- public event Action? VisibilityChanged;
+ public event Action? Changed;
+ public event Action? VisibilityChanged;
- public string Id { get; }
- public bool Locked { get; set; }
- public bool Visible
+ public string Id { get; }
+ public bool Locked { get; set; }
+ public bool Visible
+ {
+ get => _visible;
+ set
{
- get => _visible;
- set
- {
- if (_visible == value)
- return;
+ if (_visible == value)
+ return;
- _visible = value;
- VisibilityChanged?.Invoke(this);
- }
+ _visible = value;
+ VisibilityChanged?.Invoke(this);
}
-
- public virtual void Refresh() => Changed?.Invoke(this);
}
+
+ public virtual void Refresh() => Changed?.Invoke(this);
}
diff --git a/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs b/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs
index f1aed7f34..bcd1be31d 100644
--- a/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/Base/MovableModel.cs
@@ -1,31 +1,30 @@
using System;
using Blazor.Diagrams.Core.Geometry;
-namespace Blazor.Diagrams.Core.Models.Base
+namespace Blazor.Diagrams.Core.Models.Base;
+
+// I'm assuming that all movable models (nodes & groups for now) are also selectable,
+// I believe it makes sense since if you click to move something then you're also selecting
+public abstract class MovableModel : SelectableModel
{
- // I'm assuming that all movable models (nodes & groups for now) are also selectable,
- // I believe it makes sense since if you click to move something then you're also selecting
- public abstract class MovableModel : SelectableModel
+ public event Action? Moved;
+
+ public MovableModel(Point? position = null)
{
- public event Action? Moved;
-
- public MovableModel(Point? position = null)
- {
- Position = position ?? Point.Zero;
- }
+ Position = position ?? Point.Zero;
+ }
- public MovableModel(string id, Point? position = null) : base(id)
- {
- Position = position ?? Point.Zero;
- }
+ public MovableModel(string id, Point? position = null) : base(id)
+ {
+ Position = position ?? Point.Zero;
+ }
- public Point Position { get; set; }
+ public Point Position { get; set; }
- public virtual void SetPosition(double x, double y) => Position = new Point(x, y);
+ public virtual void SetPosition(double x, double y) => Position = new Point(x, y);
- ///
- /// Only use this if you know what you're doing
- ///
- public void TriggerMoved() => Moved?.Invoke(this);
- }
+ ///
+ /// Only use this if you know what you're doing
+ ///
+ public void TriggerMoved() => Moved?.Invoke(this);
}
diff --git a/src/Blazor.Diagrams.Core/Models/Base/SelectableModel.cs b/src/Blazor.Diagrams.Core/Models/Base/SelectableModel.cs
index 0e3187a13..4d1894af3 100644
--- a/src/Blazor.Diagrams.Core/Models/Base/SelectableModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/Base/SelectableModel.cs
@@ -1,29 +1,28 @@
using System;
-namespace Blazor.Diagrams.Core.Models.Base
+namespace Blazor.Diagrams.Core.Models.Base;
+
+public abstract class SelectableModel : Model
{
- public abstract class SelectableModel : Model
- {
- private int _order;
+ private int _order;
- public event Action? OrderChanged;
+ public event Action? OrderChanged;
- protected SelectableModel() { }
+ protected SelectableModel() { }
- protected SelectableModel(string id) : base(id) { }
+ protected SelectableModel(string id) : base(id) { }
- public bool Selected { get; internal set; }
- public int Order
+ public bool Selected { get; internal set; }
+ public int Order
+ {
+ get => _order;
+ set
{
- get => _order;
- set
- {
- if (value == Order)
- return;
+ if (value == Order)
+ return;
- _order = value;
- OrderChanged?.Invoke(this);
- }
+ _order = value;
+ OrderChanged?.Invoke(this);
}
}
}
diff --git a/src/Blazor.Diagrams.Core/Models/GroupModel.cs b/src/Blazor.Diagrams.Core/Models/GroupModel.cs
index 88a9636fc..e9c619e2b 100644
--- a/src/Blazor.Diagrams.Core/Models/GroupModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/GroupModel.cs
@@ -4,135 +4,134 @@
using System.Collections.Generic;
using System.Linq;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class GroupModel : NodeModel
{
- public class GroupModel : NodeModel
+ private readonly List _children;
+
+ public GroupModel(IEnumerable children, byte padding = 30, bool autoSize = true)
{
- private readonly List _children;
+ _children = new List();
- public GroupModel(IEnumerable children, byte padding = 30, bool autoSize = true)
- {
- _children = new List();
+ Size = Size.Zero;
+ Padding = padding;
+ AutoSize = autoSize;
+ Initialize(children);
+ }
- Size = Size.Zero;
- Padding = padding;
- AutoSize = autoSize;
- Initialize(children);
- }
+ public IReadOnlyList Children => _children;
+ public byte Padding { get; }
+ public bool AutoSize { get; }
- public IReadOnlyList Children => _children;
- public byte Padding { get; }
- public bool AutoSize { get; }
+ public void AddChild(NodeModel child)
+ {
+ _children.Add(child);
+ child.Group = this;
+ child.SizeChanged += OnNodeChanged;
+ child.Moving += OnNodeChanged;
- public void AddChild(NodeModel child)
+ if (UpdateDimensions())
{
- _children.Add(child);
- child.Group = this;
- child.SizeChanged += OnNodeChanged;
- child.Moving += OnNodeChanged;
-
- if (UpdateDimensions())
- {
- Refresh();
- }
+ Refresh();
}
+ }
- public void RemoveChild(NodeModel child)
- {
- if (!_children.Remove(child))
- return;
-
- child.Group = null;
- child.SizeChanged -= OnNodeChanged;
- child.Moving -= OnNodeChanged;
+ public void RemoveChild(NodeModel child)
+ {
+ if (!_children.Remove(child))
+ return;
- if (UpdateDimensions())
- {
- Refresh();
- RefreshLinks();
- }
- }
+ child.Group = null;
+ child.SizeChanged -= OnNodeChanged;
+ child.Moving -= OnNodeChanged;
- public override void SetPosition(double x, double y)
+ if (UpdateDimensions())
{
- Console.WriteLine($"({(Group == null ? "Parent" : "Child")}) SetPosition {x:00} {y:00}");
- var deltaX = x - Position.X;
- var deltaY = y - Position.Y;
- base.SetPosition(x, y);
-
- foreach (var node in Children)
- {
- node.UpdatePositionSilently(deltaX, deltaY);
- node.RefreshLinks();
- }
-
Refresh();
RefreshLinks();
}
+ }
+
+ public override void SetPosition(double x, double y)
+ {
+ Console.WriteLine($"({(Group == null ? "Parent" : "Child")}) SetPosition {x:00} {y:00}");
+ var deltaX = x - Position.X;
+ var deltaY = y - Position.Y;
+ base.SetPosition(x, y);
- public override void UpdatePositionSilently(double deltaX, double deltaY)
+ foreach (var node in Children)
{
- Console.WriteLine($"({(Group == null ? "Parent" : "Child")}) UpdatePositionSilently {deltaX:00} {deltaY:00}");
- base.UpdatePositionSilently(deltaX, deltaY);
+ node.UpdatePositionSilently(deltaX, deltaY);
+ node.RefreshLinks();
+ }
- foreach (var child in Children)
- child.UpdatePositionSilently(deltaX, deltaY);
+ Refresh();
+ RefreshLinks();
+ }
- Refresh();
- }
+ public override void UpdatePositionSilently(double deltaX, double deltaY)
+ {
+ Console.WriteLine($"({(Group == null ? "Parent" : "Child")}) UpdatePositionSilently {deltaX:00} {deltaY:00}");
+ base.UpdatePositionSilently(deltaX, deltaY);
- public void Ungroup()
- {
- foreach (var child in Children)
- {
- child.Group = null;
- child.SizeChanged -= OnNodeChanged;
- child.Moving -= OnNodeChanged;
- }
-
- _children.Clear();
- }
+ foreach (var child in Children)
+ child.UpdatePositionSilently(deltaX, deltaY);
- private void Initialize(IEnumerable children)
+ Refresh();
+ }
+
+ public void Ungroup()
+ {
+ foreach (var child in Children)
{
- foreach (var child in children)
- {
- _children.Add(child);
- child.Group = this;
- child.SizeChanged += OnNodeChanged;
- child.Moving += OnNodeChanged;
- }
-
- UpdateDimensions();
+ child.Group = null;
+ child.SizeChanged -= OnNodeChanged;
+ child.Moving -= OnNodeChanged;
}
- private void OnNodeChanged(NodeModel node)
+ _children.Clear();
+ }
+
+ private void Initialize(IEnumerable children)
+ {
+ foreach (var child in children)
{
- if (UpdateDimensions())
- {
- Refresh();
- }
+ _children.Add(child);
+ child.Group = this;
+ child.SizeChanged += OnNodeChanged;
+ child.Moving += OnNodeChanged;
}
- private bool UpdateDimensions()
+ UpdateDimensions();
+ }
+
+ private void OnNodeChanged(NodeModel node)
+ {
+ if (UpdateDimensions())
{
- if (Children.Count == 0)
- return true;
+ Refresh();
+ }
+ }
- if (Children.Any(n => n.Size == null))
- return false;
+ private bool UpdateDimensions()
+ {
+ if (Children.Count == 0)
+ return true;
- var bounds = Children.GetBounds();
+ if (Children.Any(n => n.Size == null))
+ return false;
- var newPosition = new Point(bounds.Left - Padding, bounds.Top - Padding);
- if (!Position.Equals(newPosition))
- {
- Position = newPosition;
- TriggerMoving();
- }
+ var bounds = Children.GetBounds();
- Size = new Size(bounds.Width + Padding * 2, bounds.Height + Padding * 2);
- return true;
+ var newPosition = new Point(bounds.Left - Padding, bounds.Top - Padding);
+ if (!Position.Equals(newPosition))
+ {
+ Position = newPosition;
+ TriggerMoving();
}
+
+ Size = new Size(bounds.Width + Padding * 2, bounds.Height + Padding * 2);
+ return true;
}
}
diff --git a/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs b/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs
index 7744e885b..79f6c5f45 100644
--- a/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/LinkLabelModel.cs
@@ -1,35 +1,34 @@
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class LinkLabelModel : Model
{
- public class LinkLabelModel : Model
+ public LinkLabelModel(BaseLinkModel parent, string id, string content, double? distance = null, Point? offset = null) : base(id)
{
- 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;
- }
+ 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; }
+ 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.Core/Models/LinkMarker.cs b/src/Blazor.Diagrams.Core/Models/LinkMarker.cs
index dc6b9f470..aee69245f 100644
--- a/src/Blazor.Diagrams.Core/Models/LinkMarker.cs
+++ b/src/Blazor.Diagrams.Core/Models/LinkMarker.cs
@@ -1,31 +1,30 @@
using System;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class LinkMarker
{
- 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 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 LinkMarker(string path, double width)
+ {
+ Path = path;
+ Width = width;
+ }
- public string Path { get; }
- public double Width { get; }
+ 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 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 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 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);
- }
+ 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 e8814d80b..0fad9b04b 100644
--- a/src/Blazor.Diagrams.Core/Models/LinkModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/LinkModel.cs
@@ -1,28 +1,27 @@
using Blazor.Diagrams.Core.Anchors;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class LinkModel : BaseLinkModel
{
- public class LinkModel : BaseLinkModel
- {
- public LinkModel(Anchor source, Anchor target) : base(source, target) { }
+ public LinkModel(Anchor source, Anchor target) : base(source, target) { }
- public LinkModel(string id, Anchor source, Anchor target) : base(id, source, target) { }
+ public LinkModel(string id, Anchor source, Anchor target) : base(id, source, target) { }
- public LinkModel(PortModel sourcePort, PortModel targetPort)
- : base(new SinglePortAnchor(sourcePort), new SinglePortAnchor(targetPort)) { }
+ public LinkModel(PortModel sourcePort, PortModel targetPort)
+ : base(new SinglePortAnchor(sourcePort), new SinglePortAnchor(targetPort)) { }
- public LinkModel(NodeModel sourceNode, NodeModel targetNode)
- : base(new ShapeIntersectionAnchor(sourceNode), new ShapeIntersectionAnchor(targetNode)) { }
+ public LinkModel(NodeModel sourceNode, NodeModel targetNode)
+ : base(new ShapeIntersectionAnchor(sourceNode), new ShapeIntersectionAnchor(targetNode)) { }
- public LinkModel(string id, PortModel sourcePort, PortModel targetPort)
- : base(id, new SinglePortAnchor(sourcePort), new SinglePortAnchor(targetPort)) { }
+ public LinkModel(string id, PortModel sourcePort, PortModel targetPort)
+ : base(id, new SinglePortAnchor(sourcePort), new SinglePortAnchor(targetPort)) { }
- public LinkModel(string id, NodeModel sourceNode, NodeModel targetNode)
- : base(id, new ShapeIntersectionAnchor(sourceNode), new ShapeIntersectionAnchor(targetNode)) { }
+ public LinkModel(string id, NodeModel sourceNode, NodeModel targetNode)
+ : base(id, new ShapeIntersectionAnchor(sourceNode), new ShapeIntersectionAnchor(targetNode)) { }
- public string? Color { get; set; }
- public string? SelectedColor { get; set; }
- public double Width { get; set; } = 2;
- }
+ 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 508acda98..1816bd8ab 100644
--- a/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/LinkVertexModel.cs
@@ -1,22 +1,21 @@
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class LinkVertexModel : MovableModel
{
- public class LinkVertexModel : MovableModel
+ public LinkVertexModel(BaseLinkModel parent, Point? position = null) : base(position)
{
- public LinkVertexModel(BaseLinkModel parent, Point? position = null) : base(position)
- {
- Parent = parent;
- }
+ Parent = parent;
+ }
- public BaseLinkModel Parent { get; }
+ public BaseLinkModel Parent { get; }
- public override void SetPosition(double x, double y)
- {
- base.SetPosition(x, y);
- Refresh();
- Parent.Refresh();
- }
+ public override void SetPosition(double x, double y)
+ {
+ base.SetPosition(x, y);
+ Refresh();
+ Parent.Refresh();
}
}
diff --git a/src/Blazor.Diagrams.Core/Models/NodeModel.cs b/src/Blazor.Diagrams.Core/Models/NodeModel.cs
index be5430a45..d3ec3f2df 100644
--- a/src/Blazor.Diagrams.Core/Models/NodeModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/NodeModel.cs
@@ -4,159 +4,159 @@
using System.Collections.Generic;
using System.Linq;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class NodeModel : MovableModel, IHasBounds, IHasShape, ILinkable
{
- public class NodeModel : MovableModel, IHasBounds, IHasShape, ILinkable
- {
- private readonly List _ports = new();
- private readonly List _links = new();
- private Size? _size;
+ private readonly List _ports = new();
+ private readonly List _links = new();
+ private Size? _size;
- public event Action? SizeChanged;
- public event Action? Moving;
+ public event Action? SizeChanged;
+ public event Action? Moving;
- public NodeModel(Point? position = null) : base(position)
- {
- }
+ public NodeModel(Point? position = null) : base(position)
+ {
+ }
- public NodeModel(string id, Point? position = null) : base(id, position)
- {
- }
+ public NodeModel(string id, Point? position = null) : base(id, position)
+ {
+ }
- public Size? Size
+ public Size? Size
+ {
+ get => _size;
+ set
{
- get => _size;
- set
- {
- if (value?.Equals(_size) == true)
- return;
-
- _size = value;
- SizeChanged?.Invoke(this);
- }
+ if (value?.Equals(_size) == true)
+ return;
+
+ _size = value;
+ SizeChanged?.Invoke(this);
}
+ }
+ public bool FixedSize { get; init; }
- public GroupModel? Group { get; internal set; }
- public string? Title { get; set; }
+ public GroupModel? Group { get; internal set; }
+ public string? Title { get; set; }
- public IReadOnlyList Ports => _ports;
- public IReadOnlyList Links => _links;
- public IEnumerable PortLinks => Ports.SelectMany(p => p.Links);
+ public IReadOnlyList Ports => _ports;
+ public IReadOnlyList Links => _links;
+ public IEnumerable PortLinks => Ports.SelectMany(p => p.Links);
- #region Ports
+ #region Ports
- public PortModel AddPort(PortModel port)
- {
- _ports.Add(port);
- return port;
- }
+ public PortModel AddPort(PortModel port)
+ {
+ _ports.Add(port);
+ return port;
+ }
- public PortModel AddPort(PortAlignment alignment = PortAlignment.Bottom)
- => AddPort(new PortModel(this, alignment, Position));
+ public PortModel AddPort(PortAlignment alignment = PortAlignment.Bottom)
+ => AddPort(new PortModel(this, alignment, Position));
- public PortModel? GetPort(PortAlignment alignment) => Ports.FirstOrDefault(p => p.Alignment == alignment);
+ public PortModel? GetPort(PortAlignment alignment) => Ports.FirstOrDefault(p => p.Alignment == alignment);
- public T? GetPort(PortAlignment alignment) where T : PortModel => (T?)GetPort(alignment);
+ public T? GetPort(PortAlignment alignment) where T : PortModel => (T?)GetPort(alignment);
- public bool RemovePort(PortModel port) => _ports.Remove(port);
+ public bool RemovePort(PortModel port) => _ports.Remove(port);
- #endregion
+ #endregion
- #region Refreshing
+ #region Refreshing
- public void RefreshAll()
- {
- Refresh();
- _ports.ForEach(p => p.RefreshAll());
- }
+ public void RefreshAll()
+ {
+ Refresh();
+ _ports.ForEach(p => p.RefreshAll());
+ }
- public void RefreshLinks()
+ public void RefreshLinks()
+ {
+ foreach (var link in Links)
{
- foreach (var link in Links)
- {
- link.Refresh();
- link.RefreshLinks();
- }
+ link.Refresh();
+ link.RefreshLinks();
}
+ }
- public void ReinitializePorts()
+ public void ReinitializePorts()
+ {
+ foreach (var port in Ports)
{
- foreach (var port in Ports)
- {
- port.Initialized = false;
- port.Refresh();
- }
+ port.Initialized = false;
+ port.Refresh();
}
+ }
- #endregion
+ #endregion
- public override void SetPosition(double x, double y)
- {
- var deltaX = x - Position.X;
- var deltaY = y - Position.Y;
- base.SetPosition(x, y);
-
- UpdatePortPositions(deltaX, deltaY);
- Refresh();
- RefreshLinks();
- Moving?.Invoke(this);
- }
+ public override void SetPosition(double x, double y)
+ {
+ var deltaX = x - Position.X;
+ var deltaY = y - Position.Y;
+ base.SetPosition(x, y);
+
+ UpdatePortPositions(deltaX, deltaY);
+ Refresh();
+ RefreshLinks();
+ Moving?.Invoke(this);
+ }
- public virtual void UpdatePositionSilently(double deltaX, double deltaY)
- {
- base.SetPosition(Position.X + deltaX, Position.Y + deltaY);
- UpdatePortPositions(deltaX, deltaY);
- Refresh();
- }
+ public virtual void UpdatePositionSilently(double deltaX, double deltaY)
+ {
+ base.SetPosition(Position.X + deltaX, Position.Y + deltaY);
+ UpdatePortPositions(deltaX, deltaY);
+ Refresh();
+ }
- public Rectangle? GetBounds() => GetBounds(false);
+ public Rectangle? GetBounds() => GetBounds(false);
- public Rectangle? GetBounds(bool includePorts)
- {
- 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);
- }
+ public Rectangle? GetBounds(bool includePorts)
+ {
+ 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);
+ }
- public virtual IShape GetShape() => Shapes.Rectangle(this);
+ public virtual IShape GetShape() => Shapes.Rectangle(this);
- public virtual bool CanAttachTo(ILinkable other) => other is not PortModel && other is not BaseLinkModel;
+ public virtual bool CanAttachTo(ILinkable other) => other is not PortModel && other is not BaseLinkModel;
- private void UpdatePortPositions(double deltaX, double deltaY)
+ private void UpdatePortPositions(double deltaX, double deltaY)
+ {
+ // Save some JS calls and update ports directly here
+ foreach (var port in _ports)
{
- // Save some JS calls and update ports directly here
- foreach (var port in _ports)
- {
- port.Position = new Point(port.Position.X + deltaX, port.Position.Y + deltaY);
- port.RefreshLinks();
- }
+ port.Position = new Point(port.Position.X + deltaX, port.Position.Y + deltaY);
+ port.RefreshLinks();
}
+ }
- protected void TriggerMoving()
- {
- Moving?.Invoke(this);
- }
+ protected void TriggerMoving()
+ {
+ Moving?.Invoke(this);
+ }
- void ILinkable.AddLink(BaseLinkModel link) => _links.Add(link);
+ void ILinkable.AddLink(BaseLinkModel link) => _links.Add(link);
- void ILinkable.RemoveLink(BaseLinkModel link) => _links.Remove(link);
- }
+ void ILinkable.RemoveLink(BaseLinkModel link) => _links.Remove(link);
}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams.Core/Models/PortAlignment.cs b/src/Blazor.Diagrams.Core/Models/PortAlignment.cs
index 60d54e441..b8d7fad32 100644
--- a/src/Blazor.Diagrams.Core/Models/PortAlignment.cs
+++ b/src/Blazor.Diagrams.Core/Models/PortAlignment.cs
@@ -1,14 +1,13 @@
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public enum PortAlignment
{
- public enum PortAlignment
- {
- Top,
- TopRight,
- Right,
- BottomRight,
- Bottom,
- BottomLeft,
- Left,
- TopLeft
- }
+ Top,
+ TopRight,
+ Right,
+ BottomRight,
+ Bottom,
+ BottomLeft,
+ Left,
+ TopLeft
}
diff --git a/src/Blazor.Diagrams.Core/Models/PortModel.cs b/src/Blazor.Diagrams.Core/Models/PortModel.cs
index e5502432f..52608d044 100644
--- a/src/Blazor.Diagrams.Core/Models/PortModel.cs
+++ b/src/Blazor.Diagrams.Core/Models/PortModel.cs
@@ -3,70 +3,69 @@
using Blazor.Diagrams.Core.Models.Base;
using System.Collections.Generic;
-namespace Blazor.Diagrams.Core.Models
+namespace Blazor.Diagrams.Core.Models;
+
+public class PortModel : Model, IHasBounds, IHasShape, ILinkable
{
- public class PortModel : Model, IHasBounds, IHasShape, ILinkable
- {
- private readonly List _links = new(4);
+ private readonly List _links = new(4);
- public PortModel(NodeModel parent, PortAlignment alignment = PortAlignment.Bottom, Point? position = null,
- Size? size = null)
- {
- Parent = parent;
- Alignment = alignment;
- Position = position ?? Point.Zero;
- Size = size ?? Size.Zero;
- }
+ public PortModel(NodeModel parent, PortAlignment alignment = PortAlignment.Bottom, Point? position = null,
+ Size? size = null)
+ {
+ Parent = parent;
+ Alignment = alignment;
+ Position = position ?? Point.Zero;
+ Size = size ?? Size.Zero;
+ }
- public PortModel(string id, NodeModel parent, PortAlignment alignment = PortAlignment.Bottom,
- Point? position = null, Size? size = null) : base(id)
- {
- Parent = parent;
- Alignment = alignment;
- Position = position ?? Point.Zero;
- Size = size ?? Size.Zero;
- }
+ public PortModel(string id, NodeModel parent, PortAlignment alignment = PortAlignment.Bottom,
+ Point? position = null, Size? size = null) : base(id)
+ {
+ Parent = parent;
+ Alignment = alignment;
+ Position = position ?? Point.Zero;
+ Size = size ?? Size.Zero;
+ }
- public NodeModel Parent { get; }
- public PortAlignment Alignment { get; }
- public Point Position { get; set; }
- public Point MiddlePosition => new(Position.X + Size.Width / 2, Position.Y + Size.Height / 2);
- public Size Size { get; set; }
- public IReadOnlyList Links => _links;
- ///
- /// 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 NodeModel Parent { get; }
+ public PortAlignment Alignment { get; }
+ public Point Position { get; set; }
+ public Point MiddlePosition => new(Position.X + Size.Width / 2, Position.Y + Size.Height / 2);
+ public Size Size { get; set; }
+ public IReadOnlyList Links => _links;
+ ///
+ /// 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()
- {
- Refresh();
- RefreshLinks();
- }
+ public void RefreshAll()
+ {
+ Refresh();
+ RefreshLinks();
+ }
- public void RefreshLinks()
+ public void RefreshLinks()
+ {
+ foreach (var link in Links)
{
- foreach (var link in Links)
- {
- link.Refresh();
- link.RefreshLinks();
- }
+ link.Refresh();
+ link.RefreshLinks();
}
+ }
- public T GetParent() where T : NodeModel => (T)Parent;
+ public T GetParent() where T : NodeModel => (T)Parent;
- public Rectangle GetBounds() => new(Position, Size);
+ public Rectangle GetBounds() => new(Position, Size);
- public virtual IShape GetShape() => Shapes.Circle(this);
+ public virtual IShape GetShape() => Shapes.Circle(this);
- public virtual bool CanAttachTo(ILinkable other)
- {
- // Todo: remove in order to support same node links
- return other is PortModel port && port != this && !port.Locked && Parent != port.Parent;
- }
+ public virtual bool CanAttachTo(ILinkable other)
+ {
+ // Todo: remove in order to support same node links
+ return other is PortModel port && port != this && !port.Locked && Parent != port.Parent;
+ }
- void ILinkable.AddLink(BaseLinkModel link) => _links.Add(link);
+ void ILinkable.AddLink(BaseLinkModel link) => _links.Add(link);
- void ILinkable.RemoveLink(BaseLinkModel link) => _links.Remove(link);
- }
+ void ILinkable.RemoveLink(BaseLinkModel link) => _links.Remove(link);
}
diff --git a/src/Blazor.Diagrams.Core/MouseEventButton.cs b/src/Blazor.Diagrams.Core/MouseEventButton.cs
index 98d353a2c..c3d6894b1 100644
--- a/src/Blazor.Diagrams.Core/MouseEventButton.cs
+++ b/src/Blazor.Diagrams.Core/MouseEventButton.cs
@@ -1,11 +1,10 @@
-namespace Blazor.Diagrams.Core
+namespace Blazor.Diagrams.Core;
+
+public enum MouseEventButton : long
{
- public enum MouseEventButton : long
- {
- Left = 0,
- Wheel = 1,
- Right = 2,
- Fourth = 4,
- Fifth = 5
- }
+ Left = 0,
+ Wheel = 1,
+ Right = 2,
+ Fourth = 4,
+ Fifth = 5
}
diff --git a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerator.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerator.cs
index afe745c30..113df2f90 100644
--- a/src/Blazor.Diagrams.Core/PathGenerators/PathGenerator.cs
+++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGenerator.cs
@@ -2,37 +2,36 @@
using Blazor.Diagrams.Core.Models.Base;
using System;
-namespace Blazor.Diagrams.Core.PathGenerators
+namespace Blazor.Diagrams.Core.PathGenerators;
+
+public abstract class PathGenerator
{
- public abstract class PathGenerator
- {
- public abstract PathGeneratorResult GetResult(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target);
+ public abstract PathGeneratorResult GetResult(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target);
- protected static double AdjustRouteForSourceMarker(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;
- }
+ protected static double AdjustRouteForSourceMarker(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;
+ }
- protected static double AdjustRouteForTargetMarker(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;
- }
+ protected static double AdjustRouteForTargetMarker(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;
+ }
- protected 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;
- }
+ protected 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/PathGenerators/PathGeneratorResult.cs b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs
index b6eb69aa0..a20b9cb64 100644
--- a/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs
+++ b/src/Blazor.Diagrams.Core/PathGenerators/PathGeneratorResult.cs
@@ -1,26 +1,25 @@
using Blazor.Diagrams.Core.Geometry;
using SvgPathProperties;
-namespace Blazor.Diagrams.Core
+namespace Blazor.Diagrams.Core;
+
+public class PathGeneratorResult
{
- public class PathGeneratorResult
+ public PathGeneratorResult(SvgPath fullPath, SvgPath[] paths, double? sourceMarkerAngle = null, Point? sourceMarkerPosition = null,
+ double? targetMarkerAngle = null, Point? targetMarkerPosition = null)
{
- public PathGeneratorResult(SvgPath fullPath, SvgPath[] paths, double? sourceMarkerAngle = null, Point? sourceMarkerPosition = null,
- double? targetMarkerAngle = null, Point? targetMarkerPosition = null)
- {
- FullPath = fullPath;
- Paths = paths;
- SourceMarkerAngle = sourceMarkerAngle;
- SourceMarkerPosition = sourceMarkerPosition;
- TargetMarkerAngle = targetMarkerAngle;
- TargetMarkerPosition = targetMarkerPosition;
- }
-
- public SvgPath FullPath { get; }
- public SvgPath[] Paths { get; }
- public double? SourceMarkerAngle { get; }
- public Point? SourceMarkerPosition { get; }
- public double? TargetMarkerAngle { get; }
- public Point? TargetMarkerPosition { get; }
+ FullPath = fullPath;
+ Paths = paths;
+ SourceMarkerAngle = sourceMarkerAngle;
+ SourceMarkerPosition = sourceMarkerPosition;
+ TargetMarkerAngle = targetMarkerAngle;
+ TargetMarkerPosition = targetMarkerPosition;
}
+
+ public SvgPath FullPath { get; }
+ public SvgPath[] Paths { 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/SmoothPathGenerator.cs b/src/Blazor.Diagrams.Core/PathGenerators/SmoothPathGenerator.cs
index 9b3162c7a..f1327e848 100644
--- a/src/Blazor.Diagrams.Core/PathGenerators/SmoothPathGenerator.cs
+++ b/src/Blazor.Diagrams.Core/PathGenerators/SmoothPathGenerator.cs
@@ -5,126 +5,125 @@
using SvgPathProperties;
using System;
-namespace Blazor.Diagrams.Core.PathGenerators
+namespace Blazor.Diagrams.Core.PathGenerators;
+
+public class SmoothPathGenerator : PathGenerator
{
- public class SmoothPathGenerator : PathGenerator
+ private readonly double _margin;
+
+ public SmoothPathGenerator(double margin = 125)
+ {
+ _margin = margin;
+ }
+
+ public override PathGeneratorResult GetResult(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target)
{
- private readonly double _margin;
+ route = ConcatRouteAndSourceAndTarget(route, source, target);
+
+ if (route.Length > 2)
+ return CurveThroughPoints(route, link);
- public SmoothPathGenerator(double margin = 125)
+ route = GetRouteWithCurvePoints(link, route);
+ double? sourceAngle = null;
+ double? targetAngle = null;
+
+ if (link.SourceMarker != null)
{
- _margin = margin;
+ sourceAngle = AdjustRouteForSourceMarker(route, link.SourceMarker.Width);
}
- public override PathGeneratorResult GetResult(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target)
+ if (link.TargetMarker != null)
{
- route = ConcatRouteAndSourceAndTarget(route, source, target);
-
- if (route.Length > 2)
- return CurveThroughPoints(route, link);
+ targetAngle = AdjustRouteForTargetMarker(route, link.TargetMarker.Width);
+ }
- route = GetRouteWithCurvePoints(link, route);
- double? sourceAngle = null;
- double? targetAngle = null;
+ var path = new SvgPath()
+ .AddMoveTo(route[0].X, route[0].Y)
+ .AddCubicBezierCurve(route[1].X, route[1].Y, route[2].X, route[2].Y, route[3].X, route[3].Y);
- if (link.SourceMarker != null)
- {
- sourceAngle = AdjustRouteForSourceMarker(route, link.SourceMarker.Width);
- }
-
- if (link.TargetMarker != null)
- {
- targetAngle = AdjustRouteForTargetMarker(route, link.TargetMarker.Width);
- }
+ return new PathGeneratorResult(path, Array.Empty(), sourceAngle, route[0], targetAngle, route[^1]);
+ }
- var path = new SvgPath()
- .AddMoveTo(route[0].X, route[0].Y)
- .AddCubicBezierCurve(route[1].X, route[1].Y, route[2].X, route[2].Y, route[3].X, route[3].Y);
+ private PathGeneratorResult CurveThroughPoints(Point[] route, BaseLinkModel link)
+ {
+ double? sourceAngle = null;
+ double? targetAngle = null;
- return new PathGeneratorResult(path, Array.Empty(), sourceAngle, route[0], targetAngle, route[^1]);
+ if (link.SourceMarker != null)
+ {
+ sourceAngle = AdjustRouteForSourceMarker(route, link.SourceMarker.Width);
}
- private PathGeneratorResult CurveThroughPoints(Point[] route, BaseLinkModel link)
+ if (link.TargetMarker != null)
{
- double? sourceAngle = null;
- double? targetAngle = null;
+ targetAngle = AdjustRouteForTargetMarker(route, link.TargetMarker.Width);
+ }
- if (link.SourceMarker != null)
- {
- sourceAngle = AdjustRouteForSourceMarker(route, link.SourceMarker.Width);
- }
+ BezierSpline.GetCurveControlPoints(route, out var firstControlPoints, out var secondControlPoints);
+ var paths = new SvgPath[firstControlPoints.Length];
+ var fullPath = new SvgPath().AddMoveTo(route[0].X, route[0].Y);
- if (link.TargetMarker != null)
- {
- targetAngle = AdjustRouteForTargetMarker(route, link.TargetMarker.Width);
- }
+ for (var i = 0; i < firstControlPoints.Length; i++)
+ {
+ var cp1 = firstControlPoints[i];
+ var cp2 = secondControlPoints[i];
+ fullPath.AddCubicBezierCurve(cp1.X, cp1.Y, cp2.X, cp2.Y, route[i + 1].X, route[i + 1].Y);
+ paths[i] = new SvgPath().AddMoveTo(route[i].X, route[i].Y).AddCubicBezierCurve(cp1.X, cp1.Y, cp2.X, cp2.Y, route[i + 1].X, route[i + 1].Y);
+ }
- BezierSpline.GetCurveControlPoints(route, out var firstControlPoints, out var secondControlPoints);
- var paths = new SvgPath[firstControlPoints.Length];
- var fullPath = new SvgPath().AddMoveTo(route[0].X, route[0].Y);
+ // Todo: adjust marker positions based on closest control points
+ return new PathGeneratorResult(fullPath, paths, sourceAngle, route[0], targetAngle, route[^1]);
+ }
- for (var i = 0; i < firstControlPoints.Length; i++)
- {
- var cp1 = firstControlPoints[i];
- var cp2 = secondControlPoints[i];
- fullPath.AddCubicBezierCurve(cp1.X, cp1.Y, cp2.X, cp2.Y, route[i + 1].X, route[i + 1].Y);
- paths[i] = new SvgPath().AddMoveTo(route[i].X, route[i].Y).AddCubicBezierCurve(cp1.X, cp1.Y, cp2.X, cp2.Y, route[i + 1].X, route[i + 1].Y);
- }
+ private 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, link.Source, route[0].X, route[0].Y, cX, cY, first: true);
+ var curvePointB = GetCurvePoint(route, link.Target, route[1].X, route[1].Y, cX, cY, first: false);
+ return new[] { route[0], curvePointA, curvePointB, route[1] };
+ }
- // Todo: adjust marker positions based on closest control points
- return new PathGeneratorResult(fullPath, paths, sourceAngle, route[0], targetAngle, route[^1]);
- }
+ private Point GetCurvePoint(Point[] route, Anchor anchor, double pX, double pY, double cX, double cY, bool first)
+ {
+ if (anchor is PositionAnchor)
+ return new Point(cX, cY);
- private Point[] GetRouteWithCurvePoints(BaseLinkModel link, Point[] route)
+ if (anchor is SinglePortAnchor spa)
{
- var cX = (route[0].X + route[1].X) / 2;
- var cY = (route[0].Y + route[1].Y) / 2;
- var curvePointA = GetCurvePoint(route, link.Source, route[0].X, route[0].Y, cX, cY, first: true);
- var curvePointB = GetCurvePoint(route, link.Target, route[1].X, route[1].Y, cX, cY, first: false);
- return new[] { route[0], curvePointA, curvePointB, route[1] };
+ return GetCurvePoint(pX, pY, cX, cY, spa.Port.Alignment);
}
-
- private Point GetCurvePoint(Point[] route, Anchor anchor, double pX, double pY, double cX, double cY, bool first)
+ else if (anchor is ShapeIntersectionAnchor or DynamicAnchor)
{
- if (anchor is PositionAnchor)
- return new Point(cX, cY);
-
- if (anchor is SinglePortAnchor spa)
+ if (Math.Abs(route[0].X - route[1].X) >= Math.Abs(route[0].Y - route[1].Y))
{
- return GetCurvePoint(pX, pY, cX, cY, spa.Port.Alignment);
- }
- else if (anchor is ShapeIntersectionAnchor or DynamicAnchor)
- {
- if (Math.Abs(route[0].X - route[1].X) >= Math.Abs(route[0].Y - route[1].Y))
- {
- return first ? new Point(cX, route[0].Y) : new Point(cX, route[1].Y);
- }
- else
- {
- return first ? new Point(route[0].X, cY) : new Point(route[1].X, cY);
- }
+ return first ? new Point(cX, route[0].Y) : new Point(cX, route[1].Y);
}
else
{
- throw new DiagramsException($"Unhandled Anchor type {anchor.GetType().Name} when trying to find curve point");
+ return first ? new Point(route[0].X, cY) : new Point(route[1].X, cY);
}
}
-
- private Point GetCurvePoint(double pX, double pY, double cX, double cY, PortAlignment? alignment)
+ else
{
- var margin = Math.Min(_margin, Math.Pow(Math.Pow(pX - cX, 2) + Math.Pow(pY - cY, 2), .5));
- return alignment switch
- {
- 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),
- };
+ throw new DiagramsException($"Unhandled Anchor type {anchor.GetType().Name} when trying to find curve point");
}
}
+
+ private 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 => 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/StraightPathGenerator.cs b/src/Blazor.Diagrams.Core/PathGenerators/StraightPathGenerator.cs
index 460e44604..ed315984b 100644
--- a/src/Blazor.Diagrams.Core/PathGenerators/StraightPathGenerator.cs
+++ b/src/Blazor.Diagrams.Core/PathGenerators/StraightPathGenerator.cs
@@ -3,87 +3,86 @@
using SvgPathProperties;
using System;
-namespace Blazor.Diagrams.Core.PathGenerators
+namespace Blazor.Diagrams.Core.PathGenerators;
+
+public class StraightPathGenerator : PathGenerator
{
- public class StraightPathGenerator : PathGenerator
+ private readonly double _radius;
+
+ public StraightPathGenerator(double radius = 0)
+ {
+ _radius = radius;
+ }
+
+ public override PathGeneratorResult GetResult(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target)
{
- private readonly double _radius;
+ route = ConcatRouteAndSourceAndTarget(route, source, target);
+
+ double? sourceAngle = null;
+ double? targetAngle = null;
- public StraightPathGenerator(double radius = 0)
+ if (link.SourceMarker != null)
{
- _radius = radius;
+ sourceAngle = AdjustRouteForSourceMarker(route, link.SourceMarker.Width);
}
- public override PathGeneratorResult GetResult(Diagram diagram, BaseLinkModel link, Point[] route, Point source, Point target)
+ if (link.TargetMarker != null)
{
- route = ConcatRouteAndSourceAndTarget(route, source, target);
-
- double? sourceAngle = null;
- double? targetAngle = null;
-
- if (link.SourceMarker != null)
- {
- sourceAngle = AdjustRouteForSourceMarker(route, link.SourceMarker.Width);
- }
-
- if (link.TargetMarker != null)
- {
- targetAngle = AdjustRouteForTargetMarker(route, link.TargetMarker.Width);
- }
+ targetAngle = AdjustRouteForTargetMarker(route, link.TargetMarker.Width);
+ }
- var paths = link.Vertices.Count > 0 ? new SvgPath[route.Length - 1] : null;
- var fullPath = new SvgPath().AddMoveTo(route[0].X, route[0].Y);
- double? secondDist = null;
- var lastPt = route[0];
+ var paths = link.Vertices.Count > 0 ? new SvgPath[route.Length - 1] : null;
+ var fullPath = new SvgPath().AddMoveTo(route[0].X, route[0].Y);
+ double? secondDist = null;
+ var lastPt = route[0];
- for (var i = 0; i < route.Length - 1; i++)
+ for (var i = 0; i < route.Length - 1; i++)
+ {
+ if (_radius > 0 && i > 0)
{
- if (_radius > 0 && i > 0)
- {
- var previous = route[i - 1];
- var current = route[i];
- var next = route[i + 1];
-
- double? firstDist = secondDist ?? (current.DistanceTo(previous) / 2);
- secondDist = current.DistanceTo(next) / 2;
+ var previous = route[i - 1];
+ var current = route[i];
+ var next = route[i + 1];
- var p1 = -Math.Min(_radius, firstDist.Value);
- var p2 = -Math.Min(_radius, secondDist.Value);
+ double? firstDist = secondDist ?? (current.DistanceTo(previous) / 2);
+ secondDist = current.DistanceTo(next) / 2;
- var fp = current.MoveAlongLine(previous, p1);
- var sp = current.MoveAlongLine(next, p2);
+ var p1 = -Math.Min(_radius, firstDist.Value);
+ var p2 = -Math.Min(_radius, secondDist.Value);
- fullPath.AddLineTo(fp.X, fp.Y).AddQuadraticBezierCurve(current.X, current.Y, sp.X, sp.Y);
+ var fp = current.MoveAlongLine(previous, p1);
+ var sp = current.MoveAlongLine(next, p2);
- if (paths != null)
- {
- paths[i - 1] = new SvgPath().AddMoveTo(lastPt.X, lastPt.Y).AddLineTo(fp.X, fp.Y).AddQuadraticBezierCurve(current.X, current.Y, sp.X, sp.Y);
- }
+ fullPath.AddLineTo(fp.X, fp.Y).AddQuadraticBezierCurve(current.X, current.Y, sp.X, sp.Y);
- lastPt = sp;
+ if (paths != null)
+ {
+ paths[i - 1] = new SvgPath().AddMoveTo(lastPt.X, lastPt.Y).AddLineTo(fp.X, fp.Y).AddQuadraticBezierCurve(current.X, current.Y, sp.X, sp.Y);
+ }
- if (i == route.Length - 2)
- {
- fullPath.AddLineTo(route[^1].X, route[^1].Y);
+ lastPt = sp;
- if (paths != null)
- {
- paths[i] = new SvgPath().AddMoveTo(lastPt.X, lastPt.Y).AddLineTo(route[^1].X, route[^1].Y);
- }
- }
- }
- else if (_radius == 0 || route.Length == 2)
+ if (i == route.Length - 2)
{
- fullPath.AddLineTo(route[i + 1].X, route[i + 1].Y);
+ fullPath.AddLineTo(route[^1].X, route[^1].Y);
if (paths != null)
{
- paths[i] = new SvgPath().AddMoveTo(route[i].X, route[i].Y).AddLineTo(route[i + 1].X, route[i + 1].Y);
+ paths[i] = new SvgPath().AddMoveTo(lastPt.X, lastPt.Y).AddLineTo(route[^1].X, route[^1].Y);
}
}
}
+ else if (_radius == 0 || route.Length == 2)
+ {
+ fullPath.AddLineTo(route[i + 1].X, route[i + 1].Y);
- return new PathGeneratorResult(fullPath, paths ?? Array.Empty(), sourceAngle, route[0], targetAngle, route[^1]);
+ if (paths != null)
+ {
+ paths[i] = new SvgPath().AddMoveTo(route[i].X, route[i].Y).AddLineTo(route[i + 1].X, route[i + 1].Y);
+ }
+ }
}
+
+ return new PathGeneratorResult(fullPath, paths ?? Array.Empty(), sourceAngle, route[0], targetAngle, route[^1]);
}
}
diff --git a/src/Blazor.Diagrams.Core/Routers/NormalRouter.cs b/src/Blazor.Diagrams.Core/Routers/NormalRouter.cs
index f20a1ac31..e7e94f98c 100644
--- a/src/Blazor.Diagrams.Core/Routers/NormalRouter.cs
+++ b/src/Blazor.Diagrams.Core/Routers/NormalRouter.cs
@@ -2,13 +2,12 @@
using Blazor.Diagrams.Core.Models.Base;
using System.Linq;
-namespace Blazor.Diagrams.Core.Routers
+namespace Blazor.Diagrams.Core.Routers;
+
+public class NormalRouter : Router
{
- public class NormalRouter : Router
+ public override Point[] GetRoute(Diagram diagram, BaseLinkModel link)
{
- public override Point[] GetRoute(Diagram diagram, BaseLinkModel link)
- {
- return link.Vertices.Select(v => v.Position).ToArray();
- }
+ return link.Vertices.Select(v => v.Position).ToArray();
}
}
diff --git a/src/Blazor.Diagrams.Core/Routers/OrthogonalRouter.cs b/src/Blazor.Diagrams.Core/Routers/OrthogonalRouter.cs
index 50448e490..d37f9cd63 100644
--- a/src/Blazor.Diagrams.Core/Routers/OrthogonalRouter.cs
+++ b/src/Blazor.Diagrams.Core/Routers/OrthogonalRouter.cs
@@ -6,446 +6,445 @@
using Blazor.Diagrams.Core.Anchors;
using System.Linq;
-namespace Blazor.Diagrams.Core.Routers
+namespace Blazor.Diagrams.Core.Routers;
+
+public class OrthogonalRouter : Router
{
- public class OrthogonalRouter : Router
+ private readonly Router _fallbackRouter;
+ private double _shapeMargin;
+ private double _globalMargin;
+
+ public OrthogonalRouter(double shapeMargin = 10d, double globalMargin = 50d, Router? fallbackRouter = null)
{
- private readonly Router _fallbackRouter;
- private double _shapeMargin;
- private double _globalMargin;
+ _shapeMargin = shapeMargin;
+ _globalMargin = globalMargin;
+ _fallbackRouter = fallbackRouter ?? new NormalRouter();
+ }
- public OrthogonalRouter(double shapeMargin = 10d, double globalMargin = 50d, Router? fallbackRouter = null)
+ public override Point[] GetRoute(Diagram diagram, BaseLinkModel link)
+ {
+ if (!link.IsAttached)
+ return _fallbackRouter.GetRoute(diagram, link);
+
+ if (link.Source is not SinglePortAnchor spa1)
+ return _fallbackRouter.GetRoute(diagram, link);
+
+ if (link.Target is not SinglePortAnchor targetAnchor)
+ return _fallbackRouter.GetRoute(diagram, link);
+
+ var sourcePort = spa1.Port;
+ if (targetAnchor == null || sourcePort.Parent.Size == null || targetAnchor.Port.Parent.Size == null)
+ return _fallbackRouter.GetRoute(diagram, link);
+
+ var targetPort = targetAnchor.Port;
+
+ var shapeMargin = _shapeMargin;
+ var globalBoundsMargin = _globalMargin;
+ var spots = new HashSet();
+ var verticals = new List();
+ var horizontals = new List();
+ var sideA = sourcePort.Alignment;
+ var sideAVertical = IsVerticalSide(sideA);
+ var sideB = targetPort.Alignment;
+ var sideBVertical = IsVerticalSide(sideB);
+ var originA = GetPortPositionBasedOnAlignment(sourcePort);
+ var originB = GetPortPositionBasedOnAlignment(targetPort);
+ var shapeA = sourcePort.Parent.GetBounds(includePorts: true)!;
+ var shapeB = targetPort.Parent.GetBounds(includePorts: true)!;
+ var inflatedA = shapeA.Inflate(shapeMargin, shapeMargin);
+ var inflatedB = shapeB.Inflate(shapeMargin, shapeMargin);
+
+ if (inflatedA.Intersects(inflatedB))
{
- _shapeMargin = shapeMargin;
- _globalMargin = globalMargin;
- _fallbackRouter = fallbackRouter ?? new NormalRouter();
+ shapeMargin = 0;
+ inflatedA = shapeA;
+ inflatedB = shapeB;
}
- public override Point[] GetRoute(Diagram diagram, BaseLinkModel link)
- {
- if (!link.IsAttached)
- return _fallbackRouter.GetRoute(diagram, link);
-
- if (link.Source is not SinglePortAnchor spa1)
- return _fallbackRouter.GetRoute(diagram, link);
-
- if (link.Target is not SinglePortAnchor targetAnchor)
- return _fallbackRouter.GetRoute(diagram, link);
-
- var sourcePort = spa1.Port;
- if (targetAnchor == null || sourcePort.Parent.Size == null || targetAnchor.Port.Parent.Size == null)
- return _fallbackRouter.GetRoute(diagram, link);
-
- var targetPort = targetAnchor.Port;
-
- var shapeMargin = _shapeMargin;
- var globalBoundsMargin = _globalMargin;
- var spots = new HashSet();
- var verticals = new List();
- var horizontals = new List();
- var sideA = sourcePort.Alignment;
- var sideAVertical = IsVerticalSide(sideA);
- var sideB = targetPort.Alignment;
- var sideBVertical = IsVerticalSide(sideB);
- var originA = GetPortPositionBasedOnAlignment(sourcePort);
- var originB = GetPortPositionBasedOnAlignment(targetPort);
- var shapeA = sourcePort.Parent.GetBounds(includePorts: true)!;
- var shapeB = 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);
+ // 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);
+ // 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);
+ // 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));
+ // Points of shape antennas
+ spots.Add(GetOriginSpot(originA, sideA, shapeMargin));
+ spots.Add(GetOriginSpot(originB, sideB, shapeMargin));
- // Sort rulers
- verticals.Sort();
- horizontals.Sort();
+ // Sort rulers
+ verticals.Sort();
+ horizontals.Sort();
- // Create grid
- var grid = RulersToGrid(verticals, horizontals, bounds);
- var gridPoints = GridToSpots(grid, new[] { inflatedA, inflatedB });
+ // Create grid
+ var grid = RulersToGrid(verticals, horizontals, bounds);
+ var gridPoints = GridToSpots(grid, new[] { inflatedA, inflatedB });
- // Add to spots
- spots.UnionWith(gridPoints);
+ // Add to spots
+ spots.UnionWith(gridPoints);
- var ys = spots.Select(p => p.Y).Distinct().ToList();
- var xs = spots.Select(p => p.X).Distinct().ToList();
- ys.Sort();
- xs.Sort();
+ var ys = spots.Select(p => p.Y).Distinct().ToList();
+ var xs = spots.Select(p => p.X).Distinct().ToList();
+ ys.Sort();
+ xs.Sort();
- var nodes = spots.ToDictionary(p => p, p => new Node(p));
+ var nodes = spots.ToDictionary(p => p, p => new Node(p));
- for (var i = 0; i < ys.Count; i++)
+ for (var i = 0; i < ys.Count; i++)
+ {
+ for (var j = 0; j < xs.Count; j++)
{
- for (var j = 0; j < xs.Count; j++)
+ var b = new Point(xs[j], ys[i]);
+ if (!nodes.ContainsKey(b))
+ continue;
+
+ if (j > 0)
{
- var b = new Point(xs[j], ys[i]);
- if (!nodes.ContainsKey(b))
- continue;
+ var a = new Point(xs[j - 1], ys[i]);
- if (j > 0)
+ if (nodes.ContainsKey(a))
{
- var a = new Point(xs[j - 1], ys[i]);
-
- if (nodes.ContainsKey(a))
- {
- nodes[a].ConnectedTo.Add(nodes[b]);
- nodes[b].ConnectedTo.Add(nodes[a]);
- }
+ nodes[a].ConnectedTo.Add(nodes[b]);
+ nodes[b].ConnectedTo.Add(nodes[a]);
}
+ }
- if (i > 0)
- {
- var a = new Point(xs[j], ys[i - 1]);
+ if (i > 0)
+ {
+ var a = new Point(xs[j], ys[i - 1]);
- if (nodes.ContainsKey(a))
- {
- nodes[a].ConnectedTo.Add(nodes[b]);
- nodes[b].ConnectedTo.Add(nodes[a]);
- }
+ if (nodes.ContainsKey(a))
+ {
+ nodes[a].ConnectedTo.Add(nodes[b]);
+ nodes[b].ConnectedTo.Add(nodes[a]);
}
}
}
+ }
- var nodeA = nodes[GetOriginSpot(originA, sideA, shapeMargin)];
- var nodeB = nodes[GetOriginSpot(originB, sideB, shapeMargin)];
- var path = AStarPathfinder.GetPath(nodeA, nodeB);
+ var nodeA = nodes[GetOriginSpot(originA, sideA, shapeMargin)];
+ var nodeB = nodes[GetOriginSpot(originB, sideB, shapeMargin)];
+ var path = AStarPathfinder.GetPath(nodeA, nodeB);
- if (path.Count > 0)
- {
- return path.ToArray();
- }
- else
- {
- return _fallbackRouter.GetRoute(diagram, link);
- }
+ if (path.Count > 0)
+ {
+ return path.ToArray();
}
-
- private static Grid RulersToGrid(List verticals, List horizontals, Rectangle bounds)
+ else
{
- var result = new Grid();
- verticals.Sort();
- horizontals.Sort();
+ return _fallbackRouter.GetRoute(diagram, link);
+ }
+ }
- var lastX = bounds.Left;
- var lastY = bounds.Top;
- var column = 0;
- var row = 0;
+ private static Grid RulersToGrid(List verticals, List horizontals, Rectangle bounds)
+ {
+ var result = new Grid();
+ verticals.Sort();
+ horizontals.Sort();
- foreach (var y in horizontals)
- {
- foreach (var x in verticals)
- {
- result.Set(row, column++, new Rectangle(lastX, lastY, x, y));
- lastX = x;
- }
+ var lastX = bounds.Left;
+ var lastY = bounds.Top;
+ var column = 0;
+ var row = 0;
- // Last cell of the row
- result.Set(row, column, new Rectangle(lastX, lastY, bounds.Right, y));
- lastX = bounds.Left;
- lastY = y;
- column = 0;
- row++;
+ 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++;
+ }
- // Last fow of cells
- foreach (var x in verticals)
+ 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 HashSet GridToSpots(Grid grid, Rectangle[] obstacles)
+ {
+ bool IsInsideObstacles(Point p)
+ {
+ foreach (var obstacle in obstacles)
{
- result.Set(row, column++, new Rectangle(lastX, lastY, x, bounds.Bottom));
- lastX = x;
+ if (obstacle.ContainsPoint(p))
+ return true;
}
- // Last cell of last row
- result.Set(row, column, new Rectangle(lastX, lastY, bounds.Right, bounds.Bottom));
- return result;
+ return false;
}
- private static HashSet GridToSpots(Grid grid, Rectangle[] obstacles)
+ void AddIfNotInsideObstacles(HashSet list, Point p)
{
- bool IsInsideObstacles(Point p)
+ if (!IsInsideObstacles(p))
{
- foreach (var obstacle in obstacles)
- {
- if (obstacle.ContainsPoint(p))
- return true;
- }
-
- return false;
+ list.Add(p);
}
+ }
+
+ var gridPoints = new HashSet();
+ foreach (var (row, data) in grid.Data)
+ {
+ var firstRow = row == 0;
+ var lastRow = row == grid.Rows - 1;
- void AddIfNotInsideObstacles(HashSet list, Point p)
+ foreach (var (col, r) in data)
{
- if (!IsInsideObstacles(p))
+ 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)
{
- list.Add(p);
+ AddIfNotInsideObstacles(gridPoints, r.NorthWest);
+ AddIfNotInsideObstacles(gridPoints, r.NorthEast);
+ AddIfNotInsideObstacles(gridPoints, r.SouthWest);
+ AddIfNotInsideObstacles(gridPoints, r.SouthEast);
}
- }
-
- var gridPoints = new HashSet();
- foreach (var (row, data) in grid.Data)
- {
- var firstRow = row == 0;
- var lastRow = row == grid.Rows - 1;
-
- foreach (var (col, r) in data)
+ else if (firstRow)
{
- 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)
- {
- AddIfNotInsideObstacles(gridPoints, r.NorthWest);
- AddIfNotInsideObstacles(gridPoints, r.NorthEast);
- AddIfNotInsideObstacles(gridPoints, r.SouthWest);
- AddIfNotInsideObstacles(gridPoints, r.SouthEast);
- }
- else if (firstRow)
- {
- AddIfNotInsideObstacles(gridPoints, r.NorthWest);
- AddIfNotInsideObstacles(gridPoints, r.North);
- AddIfNotInsideObstacles(gridPoints, r.NorthEast);
- }
- else if (lastRow)
- {
- AddIfNotInsideObstacles(gridPoints, r.SouthEast);
- AddIfNotInsideObstacles(gridPoints, r.South);
- AddIfNotInsideObstacles(gridPoints, r.SouthWest);
- }
- else if (firstCol)
- {
- AddIfNotInsideObstacles(gridPoints, r.NorthWest);
- AddIfNotInsideObstacles(gridPoints, r.West);
- AddIfNotInsideObstacles(gridPoints, r.SouthWest);
- }
- else if (lastCol)
- {
- AddIfNotInsideObstacles(gridPoints, r.NorthEast);
- AddIfNotInsideObstacles(gridPoints, r.East);
- AddIfNotInsideObstacles(gridPoints, r.SouthEast);
- }
- else
- {
- AddIfNotInsideObstacles(gridPoints, r.NorthWest);
- AddIfNotInsideObstacles(gridPoints, r.North);
- AddIfNotInsideObstacles(gridPoints, r.NorthEast);
- AddIfNotInsideObstacles(gridPoints, r.East);
- AddIfNotInsideObstacles(gridPoints, r.SouthEast);
- AddIfNotInsideObstacles(gridPoints, r.South);
- AddIfNotInsideObstacles(gridPoints, r.SouthWest);
- AddIfNotInsideObstacles(gridPoints, r.West);
- AddIfNotInsideObstacles(gridPoints, r.Center);
- }
+ AddIfNotInsideObstacles(gridPoints, r.NorthWest);
+ AddIfNotInsideObstacles(gridPoints, r.North);
+ AddIfNotInsideObstacles(gridPoints, r.NorthEast);
+ }
+ else if (lastRow)
+ {
+ AddIfNotInsideObstacles(gridPoints, r.SouthEast);
+ AddIfNotInsideObstacles(gridPoints, r.South);
+ AddIfNotInsideObstacles(gridPoints, r.SouthWest);
+ }
+ else if (firstCol)
+ {
+ AddIfNotInsideObstacles(gridPoints, r.NorthWest);
+ AddIfNotInsideObstacles(gridPoints, r.West);
+ AddIfNotInsideObstacles(gridPoints, r.SouthWest);
+ }
+ else if (lastCol)
+ {
+ AddIfNotInsideObstacles(gridPoints, r.NorthEast);
+ AddIfNotInsideObstacles(gridPoints, r.East);
+ AddIfNotInsideObstacles(gridPoints, r.SouthEast);
+ }
+ else
+ {
+ AddIfNotInsideObstacles(gridPoints, r.NorthWest);
+ AddIfNotInsideObstacles(gridPoints, r.North);
+ AddIfNotInsideObstacles(gridPoints, r.NorthEast);
+ AddIfNotInsideObstacles(gridPoints, r.East);
+ AddIfNotInsideObstacles(gridPoints, r.SouthEast);
+ AddIfNotInsideObstacles(gridPoints, r.South);
+ AddIfNotInsideObstacles(gridPoints, r.SouthWest);
+ AddIfNotInsideObstacles(gridPoints, r.West);
+ AddIfNotInsideObstacles(gridPoints, r.Center);
}
}
-
- // Reduce repeated points and filter out those who touch shapes
- return gridPoints;
}
- private static bool IsVerticalSide(PortAlignment alignment)
- => alignment == PortAlignment.Top || alignment == PortAlignment.Bottom;
-
- 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()
- };
- }
+ // Reduce repeated points and filter out those who touch shapes
+ return gridPoints;
}
- class Grid
+ private static bool IsVerticalSide(PortAlignment alignment)
+ => alignment == PortAlignment.Top || alignment == PortAlignment.Bottom;
+
+ private static Point GetOriginSpot(Point p, PortAlignment alignment, double shapeMargin)
{
- public Grid()
+ return alignment switch
{
- Data = new Dictionary>();
- }
+ 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()
+ };
+ }
+}
- public Dictionary> Data { get; }
- public double Rows { get; private set; }
- public double Columns { get; private set; }
+class Grid
+{
+ public Grid()
+ {
+ Data = new Dictionary>();
+ }
- public void Set(double row, double column, Rectangle rectangle)
- {
- Rows = Math.Max(Rows, row + 1);
- Columns = Math.Max(Columns, column + 1);
+ public Dictionary> Data { get; }
+ public double Rows { get; private set; }
+ public double Columns { get; private set; }
- if (!Data.ContainsKey(row))
- {
- Data.Add(row, new Dictionary());
- }
+ public void Set(double row, double column, Rectangle rectangle)
+ {
+ Rows = Math.Max(Rows, row + 1);
+ Columns = Math.Max(Columns, column + 1);
- Data[row].Add(column, rectangle);
+ if (!Data.ContainsKey(row))
+ {
+ Data.Add(row, new Dictionary());
}
+
+ Data[row].Add(column, rectangle);
}
+}
- static class AStarPathfinder
+static class AStarPathfinder
+{
+ public static IReadOnlyList GetPath(Node start, Node goal)
{
- public static IReadOnlyList GetPath(Node start, Node goal)
- {
- var frontier = new PriorityQueue();
- frontier.Enqueue(start, 0);
+ var frontier = new PriorityQueue();
+ frontier.Enqueue(start, 0);
- while (frontier.Count > 0)
- {
- var current = frontier.Dequeue();
+ while (frontier.Count > 0)
+ {
+ var current = frontier.Dequeue();
- if (current.Equals(goal))
- break;
+ if (current.Equals(goal))
+ break;
- foreach (var next in current.ConnectedTo)
+ foreach (var next in current.ConnectedTo)
+ {
+ var newCost = current.Cost + 1.0;
+ if (current.Parent != null && IsChangeOfDirection(current.Parent.Position, current.Position, next.Position))
{
- var newCost = current.Cost + 1.0;
- if (current.Parent != null && IsChangeOfDirection(current.Parent.Position, current.Position, next.Position))
- {
- newCost *= newCost;
- newCost *= newCost;
- }
-
- if (next.Cost == 0 || newCost < next.Cost)
- {
- next.Cost = newCost;
- var priority = newCost + Heuristic(next.Position, goal.Position);
- frontier.Enqueue(next, priority);
- next.Parent = current;
- }
+ newCost *= newCost;
+ newCost *= newCost;
}
- }
-
- var result = new List();
- var c = goal.Parent;
-
- while (c != null && c != start)
- {
- result.Insert(0, c.Position);
- c = c.Parent;
- }
- if (c == start)
- {
- result = SimplifyPath(result);
-
- // In case of paths with one bend
- if (result.Count > 2)
+ if (next.Cost == 0 || newCost < next.Cost)
{
- if (AreOnSameLine(result[^2], result[^1], goal.Position))
- {
- result.RemoveAt(result.Count - 1);
- }
-
- if (AreOnSameLine(start.Position, result[0], result[1]))
- {
- result.RemoveAt(0);
- }
+ next.Cost = newCost;
+ var priority = newCost + Heuristic(next.Position, goal.Position);
+ frontier.Enqueue(next, priority);
+ next.Parent = current;
}
-
- return result;
- }
- else
- {
- return Array.Empty();
}
}
- private static bool AreOnSameLine(Point prev, Point curr, Point next)
+ var result = new List();
+ var c = goal.Parent;
+
+ while (c != null && c != start)
{
- return (prev.X == curr.X && curr.X == next.X) || (prev.Y == curr.Y && curr.Y == next.Y);
+ result.Insert(0, c.Position);
+ c = c.Parent;
}
- private static List SimplifyPath(List path)
+ if (c == start)
{
- for (var i = path.Count - 2; i > 0; i--)
+ result = SimplifyPath(result);
+
+ // In case of paths with one bend
+ if (result.Count > 2)
{
- var prev = path[i + 1];
- var curr = path[i];
- var next = path[i - 1];
+ if (AreOnSameLine(result[^2], result[^1], goal.Position))
+ {
+ result.RemoveAt(result.Count - 1);
+ }
- if (AreOnSameLine(prev, curr, next))
+ if (AreOnSameLine(start.Position, result[0], result[1]))
{
- path.RemoveAt(i);
+ result.RemoveAt(0);
}
}
- return path;
+ return result;
}
-
- private static bool IsChangeOfDirection(Point a, Point b, Point c)
+ else
{
- if (a.X == b.X && b.X != c.X)
- return true;
-
- if (a.Y == b.Y && b.Y != c.Y)
- return true;
-
- return false;
+ return Array.Empty();
}
+ }
- private static double Heuristic(Point a, Point b)
- {
- return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
- }
+ private static bool AreOnSameLine(Point prev, Point curr, Point next)
+ {
+ return (prev.X == curr.X && curr.X == next.X) || (prev.Y == curr.Y && curr.Y == next.Y);
}
- class Node
+ private static List SimplifyPath(List path)
{
- public Node(Point position)
+ for (var i = path.Count - 2; i > 0; i--)
{
- Position = position;
- ConnectedTo = new List();
+ var prev = path[i + 1];
+ var curr = path[i];
+ var next = path[i - 1];
+
+ if (AreOnSameLine(prev, curr, next))
+ {
+ path.RemoveAt(i);
+ }
}
- public Point Position { get; }
- public List ConnectedTo { get; }
+ return path;
+ }
- public double Cost { get; internal set; }
- public Node? Parent { get; internal set; }
+ private static bool IsChangeOfDirection(Point a, Point b, Point c)
+ {
+ if (a.X == b.X && b.X != c.X)
+ return true;
- public override bool Equals(object? obj)
- {
- if (obj is not Node item)
- return false;
+ if (a.Y == b.Y && b.Y != c.Y)
+ return true;
- return Position.Equals(item.Position);
- }
+ return false;
+ }
- public override int GetHashCode()
- {
- return Position.GetHashCode();
- }
+ private static double Heuristic(Point a, Point b)
+ {
+ return Math.Abs(a.X - b.X) + Math.Abs(a.Y - b.Y);
+ }
+}
+
+class Node
+{
+ public Node(Point position)
+ {
+ Position = position;
+ ConnectedTo = new List();
+ }
+
+ public Point Position { get; }
+ public List ConnectedTo { get; }
+
+ public double Cost { get; internal set; }
+ public Node? Parent { get; internal set; }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is not Node item)
+ return false;
+
+ return Position.Equals(item.Position);
+ }
+
+ public override int GetHashCode()
+ {
+ return Position.GetHashCode();
}
}
diff --git a/src/Blazor.Diagrams.Core/Routers/Router.cs b/src/Blazor.Diagrams.Core/Routers/Router.cs
index d5a7d1d53..687d19c61 100644
--- a/src/Blazor.Diagrams.Core/Routers/Router.cs
+++ b/src/Blazor.Diagrams.Core/Routers/Router.cs
@@ -2,34 +2,33 @@
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
-namespace Blazor.Diagrams.Core.Routers
+namespace Blazor.Diagrams.Core.Routers;
+
+public abstract class Router
{
- public abstract class Router
- {
- public abstract Point[] GetRoute(Diagram diagram, BaseLinkModel link);
+ public abstract Point[] GetRoute(Diagram diagram, BaseLinkModel link);
- protected static Point GetPortPositionBasedOnAlignment(PortModel port)
+ protected static Point GetPortPositionBasedOnAlignment(PortModel port)
+ {
+ var pt = port.Position;
+ switch (port.Alignment)
{
- 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;
- }
+ 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.Core/Utils/KeysUtils.cs b/src/Blazor.Diagrams.Core/Utils/KeysUtils.cs
index eefbab74c..01950b9b8 100644
--- a/src/Blazor.Diagrams.Core/Utils/KeysUtils.cs
+++ b/src/Blazor.Diagrams.Core/Utils/KeysUtils.cs
@@ -1,19 +1,18 @@
using System.Text;
-namespace Blazor.Diagrams.Core.Utils
+namespace Blazor.Diagrams.Core.Utils;
+
+public static class KeysUtils
{
- public static class KeysUtils
+ public static string GetStringRepresentation(bool ctrl, bool shift, bool alt, string key)
{
- public static string GetStringRepresentation(bool ctrl, bool shift, bool alt, string key)
- {
- var sb = new StringBuilder();
+ var sb = new StringBuilder();
- if (ctrl) sb.Append("Ctrl+");
- if (shift) sb.Append("Shift+");
- if (alt) sb.Append("Alt+");
- sb.Append(key);
+ if (ctrl) sb.Append("Ctrl+");
+ if (shift) sb.Append("Shift+");
+ if (alt) sb.Append("Alt+");
+ sb.Append(key);
- return sb.ToString();
- }
+ return sb.ToString();
}
}
diff --git a/src/Blazor.Diagrams/Components/Renderers/LinkVertexRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/LinkVertexRenderer.cs
index 023aac91f..7fa149d22 100644
--- a/src/Blazor.Diagrams/Components/Renderers/LinkVertexRenderer.cs
+++ b/src/Blazor.Diagrams/Components/Renderers/LinkVertexRenderer.cs
@@ -7,90 +7,89 @@
using Blazor.Diagrams.Core.Extensions;
using Microsoft.AspNetCore.Components.Rendering;
-namespace Blazor.Diagrams.Components.Renderers
+namespace Blazor.Diagrams.Components.Renderers;
+
+public class LinkVertexRenderer : ComponentBase, IDisposable
{
- public class LinkVertexRenderer : ComponentBase, IDisposable
- {
- private bool _shouldRender = true;
+ private bool _shouldRender = true;
- [CascadingParameter] public BlazorDiagram BlazorDiagram { get; set; } = null!;
- [Parameter] public LinkVertexModel Vertex { get; set; } = null!;
- [Parameter] public string? Color { get; set; }
- [Parameter] public string? SelectedColor { get; set; }
+ [CascadingParameter] public BlazorDiagram BlazorDiagram { get; set; } = null!;
+ [Parameter] public LinkVertexModel Vertex { get; set; } = null!;
+ [Parameter] public string? Color { get; set; }
+ [Parameter] public string? SelectedColor { get; set; }
- private string? ColorToUse => Vertex.Selected ? SelectedColor : Color;
+ private string? ColorToUse => Vertex.Selected ? SelectedColor : Color;
- public void Dispose()
- {
- Vertex.Changed -= OnVertexChanged;
- }
+ public void Dispose()
+ {
+ Vertex.Changed -= OnVertexChanged;
+ }
- protected override void OnInitialized()
- {
- Vertex.Changed += OnVertexChanged;
- }
+ protected override void OnInitialized()
+ {
+ Vertex.Changed += OnVertexChanged;
+ }
- protected override bool ShouldRender()
- {
- if (!_shouldRender) return false;
+ protected override bool ShouldRender()
+ {
+ if (!_shouldRender) return false;
- _shouldRender = false;
- return true;
- }
+ _shouldRender = false;
+ return true;
+ }
- protected override void BuildRenderTree(RenderTreeBuilder builder)
- {
- var componentType = BlazorDiagram.GetComponent(Vertex);
-
- builder.OpenElement(0, "g");
- builder.AddAttribute(1, "class", "diagram-link-vertex");
- builder.AddAttribute(4, "cursor", "move");
- builder.AddAttribute(5, "ondblclick", value: EventCallback.Factory.Create(this, OnDoubleClick));
- builder.AddAttribute(6, "onpointerdown", EventCallback.Factory.Create(this, OnPointerDown));
- builder.AddAttribute(7, "onpointerup", EventCallback.Factory.Create(this, OnPointerUp));
- builder.AddEventStopPropagationAttribute(8, "onpointerdown", true);
- builder.AddEventStopPropagationAttribute(9, "onpointerup", true);
-
- if (componentType == null)
- {
- builder.OpenElement(10, "circle");
- builder.AddAttribute(11, "cx", Vertex.Position.X.ToInvariantString());
- builder.AddAttribute(12, "cy", Vertex.Position.Y.ToInvariantString());
- builder.AddAttribute(13, "r", "5");
- builder.AddAttribute(14, "fill", ColorToUse);
- builder.CloseElement();
- }
- else
- {
- builder.OpenComponent(15, componentType);
- builder.AddAttribute(16, "Vertex", Vertex);
- builder.AddAttribute(17, "Color", ColorToUse);
- builder.CloseComponent();
- }
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ var componentType = BlazorDiagram.GetComponent(Vertex);
- builder.CloseElement();
- }
+ builder.OpenElement(0, "g");
+ builder.AddAttribute(1, "class", "diagram-link-vertex");
+ builder.AddAttribute(4, "cursor", "move");
+ builder.AddAttribute(5, "ondblclick", value: EventCallback.Factory.Create(this, OnDoubleClick));
+ builder.AddAttribute(6, "onpointerdown", EventCallback.Factory.Create(this, OnPointerDown));
+ builder.AddAttribute(7, "onpointerup", EventCallback.Factory.Create(this, OnPointerUp));
+ builder.AddEventStopPropagationAttribute(8, "onpointerdown", true);
+ builder.AddEventStopPropagationAttribute(9, "onpointerup", true);
- private void OnVertexChanged(Model _)
+ if (componentType == null)
{
- _shouldRender = true;
- InvokeAsync(StateHasChanged);
+ builder.OpenElement(10, "circle");
+ builder.AddAttribute(11, "cx", Vertex.Position.X.ToInvariantString());
+ builder.AddAttribute(12, "cy", Vertex.Position.Y.ToInvariantString());
+ builder.AddAttribute(13, "r", "5");
+ builder.AddAttribute(14, "fill", ColorToUse);
+ builder.CloseElement();
}
-
- private void OnPointerDown(PointerEventArgs e)
+ else
{
- BlazorDiagram.TriggerPointerDown(Vertex, e.ToCore());
+ builder.OpenComponent(15, componentType);
+ builder.AddAttribute(16, "Vertex", Vertex);
+ builder.AddAttribute(17, "Color", ColorToUse);
+ builder.CloseComponent();
}
- private void OnPointerUp(PointerEventArgs e)
- {
- BlazorDiagram.TriggerPointerUp(Vertex, e.ToCore());
- }
+ builder.CloseElement();
+ }
- private void OnDoubleClick(MouseEventArgs e)
- {
- Vertex.Parent.Vertices.Remove(Vertex);
- Vertex.Parent.Refresh();
- }
+ private void OnVertexChanged(Model _)
+ {
+ _shouldRender = true;
+ InvokeAsync(StateHasChanged);
+ }
+
+ private void OnPointerDown(PointerEventArgs e)
+ {
+ BlazorDiagram.TriggerPointerDown(Vertex, e.ToCore());
+ }
+
+ private void OnPointerUp(PointerEventArgs e)
+ {
+ BlazorDiagram.TriggerPointerUp(Vertex, e.ToCore());
+ }
+
+ private void OnDoubleClick(MouseEventArgs e)
+ {
+ Vertex.Parent.Vertices.Remove(Vertex);
+ Vertex.Parent.Refresh();
}
}
diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs
index 9778b46b7..0016a23f8 100644
--- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs
+++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs
@@ -33,8 +33,10 @@ public void Dispose()
Node.Changed -= OnNodeChanged;
Node.VisibilityChanged -= OnVisibilityChanged;
- if (_element.Id != null)
+ if (_element.Id != null && !Node.FixedSize)
+ {
_ = JsRuntime.UnobserveResizes(_element);
+ }
_reference?.Dispose();
}
@@ -49,7 +51,9 @@ public void OnResize(Size size)
size = new Size(size.Width / BlazorDiagram.Zoom, size.Height / BlazorDiagram.Zoom);
if (Node.Size != null && Node.Size.Width.AlmostEqualTo(size.Width) &&
Node.Size.Height.AlmostEqualTo(size.Height))
+ {
return;
+ }
Node.Size = size;
Node.Refresh();
@@ -128,7 +132,11 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
if (firstRender || _becameVisible)
{
_becameVisible = false;
- await JsRuntime.ObserveResizes(_element, _reference);
+
+ if (!Node.FixedSize)
+ {
+ await JsRuntime.ObserveResizes(_element, _reference!);
+ }
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Anchors/DynamicAnchorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Anchors/DynamicAnchorTests.cs
index 7e13fb205..97a7460b4 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Anchors/DynamicAnchorTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Anchors/DynamicAnchorTests.cs
@@ -5,193 +5,192 @@
using FluentAssertions;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Anchors
+namespace Blazor.Diagrams.Core.Tests.Anchors;
+
+public class DynamicAnchorTests
{
- public class DynamicAnchorTests
+ [Fact]
+ public void GetPosition_ShouldReturnNull_WhenNodesSizeIsNull()
+ {
+ // Arrange
+ var node = new NodeModel(position: new Point(120, 95))
+ {
+ Size = null
+ };
+ var providers = new[]
+ {
+ new BoundsBasedPositionProvider(0.0, 0.0),
+ new BoundsBasedPositionProvider(0.5, 0.0),
+ new BoundsBasedPositionProvider(1.0, 0.0),
+ new BoundsBasedPositionProvider(0.0, 0.5),
+ new BoundsBasedPositionProvider(0.5, 0.5),
+ new BoundsBasedPositionProvider(1.0, 0.5),
+ new BoundsBasedPositionProvider(0.0, 1.0),
+ new BoundsBasedPositionProvider(0.5, 1.0),
+ new BoundsBasedPositionProvider(1.0, 1.0)
+ };
+ var anchor1 = new DynamicAnchor(node, providers);
+ var anchor2 = new DynamicAnchor(node, providers);
+ var link = new LinkModel(anchor1, anchor2);
+
+ // Act
+ var position = anchor1.GetPosition(link);
+
+ // Assert
+ position.Should().BeNull();
+ }
+
+ [Fact]
+ public void GetPosition_ShouldReturnClosestPositionToOtherNodesCenter_WhenRouteIsEmpty()
+ {
+ // Arrange
+ var node1 = new NodeModel(position: new Point(120, 95))
+ {
+ Size = new Size(100, 60)
+ };
+ var node2 = new NodeModel(position: new Point(200, 60))
+ {
+ Size = new Size(100, 60)
+ };
+ var positions = new[]
+ {
+ new BoundsBasedPositionProvider(0.0, 0.0), // 120,95
+ new BoundsBasedPositionProvider(0.5, 0.0), // 170,95
+ new BoundsBasedPositionProvider(1.0, 0.0), // 220,95
+ new BoundsBasedPositionProvider(0.0, 0.5), // 120,125
+ new BoundsBasedPositionProvider(0.5, 0.5), // 170,125
+ new BoundsBasedPositionProvider(1.0, 0.5), // 220,125
+ new BoundsBasedPositionProvider(0.0, 1.0), // 120,155
+ new BoundsBasedPositionProvider(0.5, 1.0), // 170,155
+ new BoundsBasedPositionProvider(1.0, 1.0) // 220,155
+ };
+ var anchor1 = new DynamicAnchor(node1, positions);
+ var anchor2 = new DynamicAnchor(node2, positions);
+ var link = new LinkModel(anchor1, anchor2);
+
+ // Act
+ var position = anchor1.GetPosition(link);
+
+ // Assert
+ position.Should().NotBeNull();
+ position!.X.Should().Be(220);
+ position.Y.Should().Be(95);
+ }
+
+ [Fact]
+ public void GetPosition_ShouldReturnClosestPositionToOtherNodesCenterWithOffset_WhenRouteIsEmpty()
{
- [Fact]
- public void GetPosition_ShouldReturnNull_WhenNodesSizeIsNull()
- {
- // Arrange
- var node = new NodeModel(position: new Point(120, 95))
- {
- Size = null
- };
- var providers = new[]
- {
- new BoundsBasedPositionProvider(0.0, 0.0),
- new BoundsBasedPositionProvider(0.5, 0.0),
- new BoundsBasedPositionProvider(1.0, 0.0),
- new BoundsBasedPositionProvider(0.0, 0.5),
- new BoundsBasedPositionProvider(0.5, 0.5),
- new BoundsBasedPositionProvider(1.0, 0.5),
- new BoundsBasedPositionProvider(0.0, 1.0),
- new BoundsBasedPositionProvider(0.5, 1.0),
- new BoundsBasedPositionProvider(1.0, 1.0)
- };
- var anchor1 = new DynamicAnchor(node, providers);
- var anchor2 = new DynamicAnchor(node, providers);
- var link = new LinkModel(anchor1, anchor2);
-
- // Act
- var position = anchor1.GetPosition(link);
-
- // Assert
- position.Should().BeNull();
- }
-
- [Fact]
- public void GetPosition_ShouldReturnClosestPositionToOtherNodesCenter_WhenRouteIsEmpty()
- {
- // Arrange
- var node1 = new NodeModel(position: new Point(120, 95))
- {
- Size = new Size(100, 60)
- };
- var node2 = new NodeModel(position: new Point(200, 60))
- {
- Size = new Size(100, 60)
- };
- var positions = new[]
- {
- new BoundsBasedPositionProvider(0.0, 0.0), // 120,95
- new BoundsBasedPositionProvider(0.5, 0.0), // 170,95
- new BoundsBasedPositionProvider(1.0, 0.0), // 220,95
- new BoundsBasedPositionProvider(0.0, 0.5), // 120,125
- new BoundsBasedPositionProvider(0.5, 0.5), // 170,125
- new BoundsBasedPositionProvider(1.0, 0.5), // 220,125
- new BoundsBasedPositionProvider(0.0, 1.0), // 120,155
- new BoundsBasedPositionProvider(0.5, 1.0), // 170,155
- new BoundsBasedPositionProvider(1.0, 1.0) // 220,155
- };
- var anchor1 = new DynamicAnchor(node1, positions);
- var anchor2 = new DynamicAnchor(node2, positions);
- var link = new LinkModel(anchor1, anchor2);
-
- // Act
- var position = anchor1.GetPosition(link);
-
- // Assert
- position.Should().NotBeNull();
- position!.X.Should().Be(220);
- position.Y.Should().Be(95);
- }
-
- [Fact]
- public void GetPosition_ShouldReturnClosestPositionToOtherNodesCenterWithOffset_WhenRouteIsEmpty()
- {
- // Arrange
- var node1 = new NodeModel(position: new Point(120, 95))
- {
- Size = new Size(100, 60)
- };
- var node2 = new NodeModel(position: new Point(200, 60))
- {
- Size = new Size(100, 60)
- };
- var positions = new[]
- {
- new BoundsBasedPositionProvider(0.0, 0.0), // 120,95
- new BoundsBasedPositionProvider(0.5, 0.0), // 170,95
- new BoundsBasedPositionProvider(1.0, 0.0, 10, -10), // 230,85
- new BoundsBasedPositionProvider(0.0, 0.5), // 120,125
- new BoundsBasedPositionProvider(0.5, 0.5), // 170,125
- new BoundsBasedPositionProvider(1.0, 0.5), // 220,125
- new BoundsBasedPositionProvider(0.0, 1.0), // 120,155
- new BoundsBasedPositionProvider(0.5, 1.0), // 170,155
- new BoundsBasedPositionProvider(1.0, 1.0) // 220,155
- };
- var anchor1 = new DynamicAnchor(node1, positions);
- var anchor2 = new DynamicAnchor(node2, positions);
- var link = new LinkModel(anchor1, anchor2);
-
- // Act
- var position = anchor1.GetPosition(link);
-
- // Assert
- position.Should().NotBeNull();
- position!.X.Should().Be(230);
- position.Y.Should().Be(85);
- }
-
- [Fact]
- public void GetPosition_ShouldReturnClosestPositionToFirstVertex_WhenRouteIsNotEmpty()
- {
- // Arrange
- var node1 = new NodeModel(position: new Point(120, 95))
- {
- Size = new Size(100, 60)
- };
- var node2 = new NodeModel(position: new Point(300, 60))
- {
- Size = new Size(100, 60)
- };
- var positions = new[]
- {
- new BoundsBasedPositionProvider(0.0, 0.0), // 120,95
- new BoundsBasedPositionProvider(0.5, 0.0), // 170,95
- new BoundsBasedPositionProvider(1.0, 0.0), // 220,95
- new BoundsBasedPositionProvider(0.0, 0.5), // 120,125
- new BoundsBasedPositionProvider(0.5, 0.5), // 170,125
- new BoundsBasedPositionProvider(1.0, 0.5), // 220,125
- new BoundsBasedPositionProvider(0.0, 1.0), // 120,155
- new BoundsBasedPositionProvider(0.5, 1.0), // 170,155
- new BoundsBasedPositionProvider(1.0, 1.0) // 220,155
- };
- var anchor1 = new DynamicAnchor(node1, positions);
- var anchor2 = new DynamicAnchor(node2, positions);
- var link = new LinkModel(anchor1, anchor2);
-
- // Act
- var position = anchor1.GetPosition(link, new Point[]
- {
- new Point(280, 115) // Vertex
- });
-
- // Assert
- position.Should().NotBeNull();
- position!.X.Should().Be(220);
- position.Y.Should().Be(125);
- }
-
- [Fact]
- public void GetPosition_ShouldReturnClosestPositionToLastVertex_WhenRouteIsNotEmptyAndIsTarget()
- {
- // Arrange
- var node1 = new NodeModel(position: new Point(120, 95))
- {
- Size = new Size(100, 60)
- };
- var node2 = new NodeModel(position: new Point(300, 60))
- {
- Size = new Size(100, 60)
- };
- var positions = new[]
- {
- new BoundsBasedPositionProvider(0.0, 0.0), // 300, 60
- new BoundsBasedPositionProvider(0.5, 0.0), // 350, 60
- new BoundsBasedPositionProvider(1.0, 0.0), // 400, 60
- new BoundsBasedPositionProvider(0.0, 0.5), // 300, 90
- new BoundsBasedPositionProvider(0.5, 0.5), // 350, 90
- new BoundsBasedPositionProvider(1.0, 0.5), // 400, 90
- new BoundsBasedPositionProvider(0.0, 1.0), // 300, 120
- new BoundsBasedPositionProvider(0.5, 1.0), // 350, 120
- new BoundsBasedPositionProvider(1.0, 1.0) // 400, 120
- };
- var anchor1 = new DynamicAnchor(node1, positions);
- var anchor2 = new DynamicAnchor(node2, positions);
- var link = new LinkModel(anchor1, anchor2);
-
- // Act
- var position = anchor2.GetPosition(link, new Point[]
- {
- new Point(280, 115) // Vertex
- });
-
- // Assert
- position.Should().NotBeNull();
- position!.X.Should().Be(300);
- position.Y.Should().Be(120);
- }
+ // Arrange
+ var node1 = new NodeModel(position: new Point(120, 95))
+ {
+ Size = new Size(100, 60)
+ };
+ var node2 = new NodeModel(position: new Point(200, 60))
+ {
+ Size = new Size(100, 60)
+ };
+ var positions = new[]
+ {
+ new BoundsBasedPositionProvider(0.0, 0.0), // 120,95
+ new BoundsBasedPositionProvider(0.5, 0.0), // 170,95
+ new BoundsBasedPositionProvider(1.0, 0.0, 10, -10), // 230,85
+ new BoundsBasedPositionProvider(0.0, 0.5), // 120,125
+ new BoundsBasedPositionProvider(0.5, 0.5), // 170,125
+ new BoundsBasedPositionProvider(1.0, 0.5), // 220,125
+ new BoundsBasedPositionProvider(0.0, 1.0), // 120,155
+ new BoundsBasedPositionProvider(0.5, 1.0), // 170,155
+ new BoundsBasedPositionProvider(1.0, 1.0) // 220,155
+ };
+ var anchor1 = new DynamicAnchor(node1, positions);
+ var anchor2 = new DynamicAnchor(node2, positions);
+ var link = new LinkModel(anchor1, anchor2);
+
+ // Act
+ var position = anchor1.GetPosition(link);
+
+ // Assert
+ position.Should().NotBeNull();
+ position!.X.Should().Be(230);
+ position.Y.Should().Be(85);
+ }
+
+ [Fact]
+ public void GetPosition_ShouldReturnClosestPositionToFirstVertex_WhenRouteIsNotEmpty()
+ {
+ // Arrange
+ var node1 = new NodeModel(position: new Point(120, 95))
+ {
+ Size = new Size(100, 60)
+ };
+ var node2 = new NodeModel(position: new Point(300, 60))
+ {
+ Size = new Size(100, 60)
+ };
+ var positions = new[]
+ {
+ new BoundsBasedPositionProvider(0.0, 0.0), // 120,95
+ new BoundsBasedPositionProvider(0.5, 0.0), // 170,95
+ new BoundsBasedPositionProvider(1.0, 0.0), // 220,95
+ new BoundsBasedPositionProvider(0.0, 0.5), // 120,125
+ new BoundsBasedPositionProvider(0.5, 0.5), // 170,125
+ new BoundsBasedPositionProvider(1.0, 0.5), // 220,125
+ new BoundsBasedPositionProvider(0.0, 1.0), // 120,155
+ new BoundsBasedPositionProvider(0.5, 1.0), // 170,155
+ new BoundsBasedPositionProvider(1.0, 1.0) // 220,155
+ };
+ var anchor1 = new DynamicAnchor(node1, positions);
+ var anchor2 = new DynamicAnchor(node2, positions);
+ var link = new LinkModel(anchor1, anchor2);
+
+ // Act
+ var position = anchor1.GetPosition(link, new Point[]
+ {
+ new Point(280, 115) // Vertex
+ });
+
+ // Assert
+ position.Should().NotBeNull();
+ position!.X.Should().Be(220);
+ position.Y.Should().Be(125);
+ }
+
+ [Fact]
+ public void GetPosition_ShouldReturnClosestPositionToLastVertex_WhenRouteIsNotEmptyAndIsTarget()
+ {
+ // Arrange
+ var node1 = new NodeModel(position: new Point(120, 95))
+ {
+ Size = new Size(100, 60)
+ };
+ var node2 = new NodeModel(position: new Point(300, 60))
+ {
+ Size = new Size(100, 60)
+ };
+ var positions = new[]
+ {
+ new BoundsBasedPositionProvider(0.0, 0.0), // 300, 60
+ new BoundsBasedPositionProvider(0.5, 0.0), // 350, 60
+ new BoundsBasedPositionProvider(1.0, 0.0), // 400, 60
+ new BoundsBasedPositionProvider(0.0, 0.5), // 300, 90
+ new BoundsBasedPositionProvider(0.5, 0.5), // 350, 90
+ new BoundsBasedPositionProvider(1.0, 0.5), // 400, 90
+ new BoundsBasedPositionProvider(0.0, 1.0), // 300, 120
+ new BoundsBasedPositionProvider(0.5, 1.0), // 350, 120
+ new BoundsBasedPositionProvider(1.0, 1.0) // 400, 120
+ };
+ var anchor1 = new DynamicAnchor(node1, positions);
+ var anchor2 = new DynamicAnchor(node2, positions);
+ var link = new LinkModel(anchor1, anchor2);
+
+ // Act
+ var position = anchor2.GetPosition(link, new Point[]
+ {
+ new Point(280, 115) // Vertex
+ });
+
+ // Assert
+ position.Should().NotBeNull();
+ position!.X.Should().Be(300);
+ position.Y.Should().Be(120);
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragNewLinkBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragNewLinkBehaviorTests.cs
index bf31df407..49df661a6 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragNewLinkBehaviorTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/DragNewLinkBehaviorTests.cs
@@ -6,440 +6,439 @@
using System.Linq;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Behaviors
+namespace Blazor.Diagrams.Core.Tests.Behaviors;
+
+public class DragNewLinkBehaviorTests
{
- public class DragNewLinkBehaviorTests
+ [Fact]
+ public void Behavior_ShouldCreateLinkWithSinglePortAnchorSource_WhenMouseDownOnPort()
{
- [Fact]
- public void Behavior_ShouldCreateLinkWithSinglePortAnchorSource_WhenMouseDownOnPort()
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var node = new NodeModel(position: new Point(100, 50));
+ var port = node.AddPort(new PortModel(node)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var node = new NodeModel(position: new Point(100, 50));
- var port = node.AddPort(new PortModel(node)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- var link = diagram.Links.Single();
- var source = link.Source as SinglePortAnchor;
- source.Should().NotBeNull();
- source!.Port.Should().BeSameAs(port);
- var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
- ongoingPosition.X.Should().Be(95);
- ongoingPosition.Y.Should().Be(95);
- }
-
- [Fact]
- public void Behavior_ShouldCreateLinkUsingFactory_WhenMouseDownOnPort()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ var link = diagram.Links.Single();
+ var source = link.Source as SinglePortAnchor;
+ source.Should().NotBeNull();
+ source!.Port.Should().BeSameAs(port);
+ var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
+ ongoingPosition.X.Should().Be(95);
+ ongoingPosition.Y.Should().Be(95);
+ }
+
+ [Fact]
+ public void Behavior_ShouldCreateLinkUsingFactory_WhenMouseDownOnPort()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var factoryCalled = false;
+ diagram.Options.Links.Factory = (d, s, ta) =>
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var factoryCalled = false;
- diagram.Options.Links.Factory = (d, s, ta) =>
- {
- factoryCalled = true;
- return new LinkModel(new SinglePortAnchor(s as PortModel), ta);
- };
- var node = new NodeModel(position: new Point(100, 50));
- var port = node.AddPort(new PortModel(node)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- factoryCalled.Should().BeTrue();
- var link = diagram.Links.Single();
- var source = link.Source as SinglePortAnchor;
- source.Should().NotBeNull();
- source!.Port.Should().BeSameAs(port);
- var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
- ongoingPosition.X.Should().Be(95);
- ongoingPosition.Y.Should().Be(95);
- }
-
- [Fact]
- public void Behavior_ShouldUpdateOngoingPosition_WhenMouseMoveIsTriggered()
+ factoryCalled = true;
+ return new LinkModel(new SinglePortAnchor(s as PortModel), ta);
+ };
+ var node = new NodeModel(position: new Point(100, 50));
+ var port = node.AddPort(new PortModel(node)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var node = new NodeModel(position: new Point(100, 50));
- var linkRefreshed = false;
- var port = node.AddPort(new PortModel(node)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- var link = diagram.Links.Single();
- link.Changed += _ => linkRefreshed = true;
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(150, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- var source = link.Source as SinglePortAnchor;
- var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
- ongoingPosition.X.Should().Be(145);
- ongoingPosition.Y.Should().Be(145);
- linkRefreshed.Should().BeTrue();
- }
-
- [Fact]
- public void Behavior_ShouldUpdateOngoingPosition_WhenMouseMoveIsTriggeredAndZoomIsChanged()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ factoryCalled.Should().BeTrue();
+ var link = diagram.Links.Single();
+ var source = link.Source as SinglePortAnchor;
+ source.Should().NotBeNull();
+ source!.Port.Should().BeSameAs(port);
+ var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
+ ongoingPosition.X.Should().Be(95);
+ ongoingPosition.Y.Should().Be(95);
+ }
+
+ [Fact]
+ public void Behavior_ShouldUpdateOngoingPosition_WhenMouseMoveIsTriggered()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var node = new NodeModel(position: new Point(100, 50));
+ var linkRefreshed = false;
+ var port = node.AddPort(new PortModel(node)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- diagram.SetZoom(1.5);
- var node = new NodeModel(position: new Point(100, 50));
- var linkRefreshed = false;
- var port = node.AddPort(new PortModel(node)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- var link = diagram.Links.Single();
- link.Changed += _ => linkRefreshed = true;
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(160, 160, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- var source = link.Source as SinglePortAnchor;
- var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
- ongoingPosition.X.Should().BeApproximately(101.6, 0.1);
- ongoingPosition.Y.Should().BeApproximately(101.6, 0.1);
- linkRefreshed.Should().BeTrue();
- }
-
- [Fact]
- public void Behavior_ShouldSnapToClosestPortAndRefreshPort_WhenSnappingIsEnabledAndPortIsInRadius()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ var link = diagram.Links.Single();
+ link.Changed += _ => linkRefreshed = true;
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(150, 150, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ var source = link.Source as SinglePortAnchor;
+ var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
+ ongoingPosition.X.Should().Be(145);
+ ongoingPosition.Y.Should().Be(145);
+ linkRefreshed.Should().BeTrue();
+ }
+
+ [Fact]
+ public void Behavior_ShouldUpdateOngoingPosition_WhenMouseMoveIsTriggeredAndZoomIsChanged()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ diagram.SetZoom(1.5);
+ var node = new NodeModel(position: new Point(100, 50));
+ var linkRefreshed = false;
+ var port = node.AddPort(new PortModel(node)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- diagram.Options.Links.EnableSnapping = true;
- diagram.Options.Links.SnappingRadius = 60;
- var node1 = new NodeModel(position: new Point(100, 50));
- var node2 = new NodeModel(position: new Point(160, 50));
- diagram.Nodes.Add(new[] { node1, node2 });
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
- var port2 = node2.AddPort(new PortModel(node2)
- {
- Initialized = true,
- Position = new Point(170, 60),
- Size = new Size(10, 20)
- });
- var port2Refreshed = false;
- port2.Changed += _ => port2Refreshed = true;
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- var link = diagram.Links.Single();
- var target = link.Target as SinglePortAnchor;
- target.Should().NotBeNull();
- target!.Port.Should().BeSameAs(port2);
- port2Refreshed.Should().BeTrue();
- }
-
- [Fact]
- public void Behavior_ShouldNotSnapToPort_WhenSnappingIsEnabledAndPortIsNotInRadius()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ var link = diagram.Links.Single();
+ link.Changed += _ => linkRefreshed = true;
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(160, 160, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ var source = link.Source as SinglePortAnchor;
+ var ongoingPosition = (link.Target as PositionAnchor)!.GetPlainPosition()!;
+ ongoingPosition.X.Should().BeApproximately(101.6, 0.1);
+ ongoingPosition.Y.Should().BeApproximately(101.6, 0.1);
+ linkRefreshed.Should().BeTrue();
+ }
+
+ [Fact]
+ public void Behavior_ShouldSnapToClosestPortAndRefreshPort_WhenSnappingIsEnabledAndPortIsInRadius()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ diagram.Options.Links.EnableSnapping = true;
+ diagram.Options.Links.SnappingRadius = 60;
+ var node1 = new NodeModel(position: new Point(100, 50));
+ var node2 = new NodeModel(position: new Point(160, 50));
+ diagram.Nodes.Add(new[] { node1, node2 });
+ var port1 = node1.AddPort(new PortModel(node1)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- diagram.Options.Links.EnableSnapping = true;
- diagram.Options.Links.SnappingRadius = 50;
- var node1 = new NodeModel(position: new Point(100, 50));
- var node2 = new NodeModel(position: new Point(160, 50));
- diagram.Nodes.Add(new[] { node1, node2 });
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
- var port2 = node2.AddPort(new PortModel(node2)
- {
- Initialized = true,
- Position = new Point(170, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(105, 105, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- var link = diagram.Links.Single();
- link.Target.Should().BeOfType();
- }
-
- [Fact]
- public void Behavior_ShouldUnSnapAndRefreshPort_WhenSnappingIsEnabledAndPortIsNotInRadiusAnymore()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+ var port2 = node2.AddPort(new PortModel(node2)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- diagram.Options.Links.EnableSnapping = true;
- diagram.Options.Links.SnappingRadius = 56;
- var node1 = new NodeModel(position: new Point(100, 50));
- var node2 = new NodeModel(position: new Point(160, 50));
- diagram.Nodes.Add(new[] { node1, node2 });
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
- var port2 = node2.AddPort(new PortModel(node2)
- {
- Initialized = true,
- Position = new Point(170, 60),
- Size = new Size(10, 20)
- });
- var port2Refreshes = 0;
- port2.Changed += _ => port2Refreshes++;
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty,
- true)); // Move towards the other port
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty,
- true)); // Move back to unsnap
-
- // Assert
- var link = diagram.Links.Single();
- var target = link.Target as SinglePortAnchor;
- target.Should().BeNull();
- port2Refreshes.Should().Be(2);
- }
-
- [Fact]
- public void Behavior_ShouldRemoveLink_WhenMouseUpOnCanvasAndRequireTargetIsTrue()
+ Initialized = true,
+ Position = new Point(170, 60),
+ Size = new Size(10, 20)
+ });
+ var port2Refreshed = false;
+ port2.Changed += _ => port2Refreshed = true;
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ var link = diagram.Links.Single();
+ var target = link.Target as SinglePortAnchor;
+ target.Should().NotBeNull();
+ target!.Port.Should().BeSameAs(port2);
+ port2Refreshed.Should().BeTrue();
+ }
+
+ [Fact]
+ public void Behavior_ShouldNotSnapToPort_WhenSnappingIsEnabledAndPortIsNotInRadius()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ diagram.Options.Links.EnableSnapping = true;
+ diagram.Options.Links.SnappingRadius = 50;
+ var node1 = new NodeModel(position: new Point(100, 50));
+ var node2 = new NodeModel(position: new Point(160, 50));
+ diagram.Nodes.Add(new[] { node1, node2 });
+ var port1 = node1.AddPort(new PortModel(node1)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var node = new NodeModel(position: new Point(100, 50));
- var port = node.AddPort(new PortModel(node)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port,
- new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerUp(null,
- new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- diagram.Links.Should().BeEmpty();
- }
-
- [Fact]
- public void Behavior_ShouldRemoveLink_WhenMouseUpOnSamePort()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+ var port2 = node2.AddPort(new PortModel(node2)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var node = new NodeModel(position: new Point(100, 50));
- var port = node.AddPort(new PortModel(node)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port,
- new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerUp(port,
- new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- diagram.Links.Should().BeEmpty();
- }
-
- [Fact]
- public void Behavior_ShouldSetTarget_WhenMouseUp()
+ Initialized = true,
+ Position = new Point(170, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(105, 105, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ var link = diagram.Links.Single();
+ link.Target.Should().BeOfType();
+ }
+
+ [Fact]
+ public void Behavior_ShouldUnSnapAndRefreshPort_WhenSnappingIsEnabledAndPortIsNotInRadiusAnymore()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ diagram.Options.Links.EnableSnapping = true;
+ diagram.Options.Links.SnappingRadius = 56;
+ var node1 = new NodeModel(position: new Point(100, 50));
+ var node2 = new NodeModel(position: new Point(160, 50));
+ diagram.Nodes.Add(new[] { node1, node2 });
+ var port1 = node1.AddPort(new PortModel(node1)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var node1 = new NodeModel(position: new Point(100, 50));
- var node2 = new NodeModel(position: new Point(160, 50));
- diagram.Nodes.Add(new[] { node1, node2 });
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
- var port2 = node2.AddPort(new PortModel(node2)
- {
- Initialized = true,
- Position = new Point(170, 60),
- Size = new Size(10, 20)
- });
- var port2Refreshes = 0;
- port2.Changed += _ => port2Refreshes++;
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerUp(port2,
- new PointerEventArgs(105, 105, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- var link = diagram.Links.Single();
- var target = link.Target as SinglePortAnchor;
- target.Should().NotBeNull();
- target!.Port.Should().BeSameAs(port2);
- port2Refreshes.Should().Be(1);
- }
-
- [Fact]
- public void Behavior_ShouldNotCreateOngoingLink_WhenFactoryReturnsNull()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+ var port2 = node2.AddPort(new PortModel(node2)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.Options.Links.Factory = (d, s, ta) => null;
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
-
- var node1 = new NodeModel(position: new Point(100, 50));
- diagram.Nodes.Add(node1);
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- diagram.Links.Should().HaveCount(0);
- }
-
- [Fact]
- public void Behavior_ShouldTriggerLinkTargetAttached_WhenMouseUpOnOtherPort()
+ Initialized = true,
+ Position = new Point(170, 60),
+ Size = new Size(10, 20)
+ });
+ var port2Refreshes = 0;
+ port2.Changed += _ => port2Refreshes++;
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty,
+ true)); // Move towards the other port
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty,
+ true)); // Move back to unsnap
+
+ // Assert
+ var link = diagram.Links.Single();
+ var target = link.Target as SinglePortAnchor;
+ target.Should().BeNull();
+ port2Refreshes.Should().Be(2);
+ }
+
+ [Fact]
+ public void Behavior_ShouldRemoveLink_WhenMouseUpOnCanvasAndRequireTargetIsTrue()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var node = new NodeModel(position: new Point(100, 50));
+ var port = node.AddPort(new PortModel(node)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- var node1 = new NodeModel(position: new Point(100, 50));
- var node2 = new NodeModel(position: new Point(160, 50));
- diagram.Nodes.Add(new[] { node1, node2 });
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
- var port2 = node2.AddPort(new PortModel(node2)
- {
- Initialized = true,
- Position = new Point(170, 60),
- Size = new Size(10, 20)
- });
- var targetAttachedTriggers = 0;
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- diagram.Links.Single().TargetAttached += _ => targetAttachedTriggers++;
-
- diagram.TriggerPointerUp(port2,
- new PointerEventArgs(105, 105, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- targetAttachedTriggers.Should().Be(1);
- }
-
- [Fact]
- public void Behavior_ShouldTriggerLinkTargetAttached_WhenLinkSnappedToPortAndMouseUp()
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port,
+ new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerUp(null,
+ new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ diagram.Links.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Behavior_ShouldRemoveLink_WhenMouseUpOnSamePort()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var node = new NodeModel(position: new Point(100, 50));
+ var port = node.AddPort(new PortModel(node)
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
- diagram.Options.Links.EnableSnapping = true;
- diagram.Options.Links.SnappingRadius = 60;
- var node1 = new NodeModel(position: new Point(100, 50));
- var node2 = new NodeModel(position: new Point(160, 50));
- diagram.Nodes.Add(new[] { node1, node2 });
- var port1 = node1.AddPort(new PortModel(node1)
- {
- Initialized = true,
- Position = new Point(110, 60),
- Size = new Size(10, 20)
- });
- var port2 = node2.AddPort(new PortModel(node2)
- {
- Initialized = true,
- Position = new Point(170, 60),
- Size = new Size(10, 20)
- });
- var targetAttachedTriggers = 0;
-
- // Act
- diagram.TriggerPointerDown(port1,
- new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- diagram.TriggerPointerMove(null,
- new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- diagram.Links.Single().TargetAttached += _ => targetAttachedTriggers++;
-
- diagram.TriggerPointerUp(null,
- new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- targetAttachedTriggers.Should().Be(1);
- }
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port,
+ new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerUp(port,
+ new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ diagram.Links.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Behavior_ShouldSetTarget_WhenMouseUp()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var node1 = new NodeModel(position: new Point(100, 50));
+ var node2 = new NodeModel(position: new Point(160, 50));
+ diagram.Nodes.Add(new[] { node1, node2 });
+ var port1 = node1.AddPort(new PortModel(node1)
+ {
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+ var port2 = node2.AddPort(new PortModel(node2)
+ {
+ Initialized = true,
+ Position = new Point(170, 60),
+ Size = new Size(10, 20)
+ });
+ var port2Refreshes = 0;
+ port2.Changed += _ => port2Refreshes++;
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerUp(port2,
+ new PointerEventArgs(105, 105, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ var link = diagram.Links.Single();
+ var target = link.Target as SinglePortAnchor;
+ target.Should().NotBeNull();
+ target!.Port.Should().BeSameAs(port2);
+ port2Refreshes.Should().Be(1);
+ }
+
+ [Fact]
+ public void Behavior_ShouldNotCreateOngoingLink_WhenFactoryReturnsNull()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.Options.Links.Factory = (d, s, ta) => null;
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+
+ var node1 = new NodeModel(position: new Point(100, 50));
+ diagram.Nodes.Add(node1);
+ var port1 = node1.AddPort(new PortModel(node1)
+ {
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ diagram.Links.Should().HaveCount(0);
+ }
+
+ [Fact]
+ public void Behavior_ShouldTriggerLinkTargetAttached_WhenMouseUpOnOtherPort()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ var node1 = new NodeModel(position: new Point(100, 50));
+ var node2 = new NodeModel(position: new Point(160, 50));
+ diagram.Nodes.Add(new[] { node1, node2 });
+ var port1 = node1.AddPort(new PortModel(node1)
+ {
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+ var port2 = node2.AddPort(new PortModel(node2)
+ {
+ Initialized = true,
+ Position = new Point(170, 60),
+ Size = new Size(10, 20)
+ });
+ var targetAttachedTriggers = 0;
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ diagram.Links.Single().TargetAttached += _ => targetAttachedTriggers++;
+
+ diagram.TriggerPointerUp(port2,
+ new PointerEventArgs(105, 105, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ targetAttachedTriggers.Should().Be(1);
+ }
+
+ [Fact]
+ public void Behavior_ShouldTriggerLinkTargetAttached_WhenLinkSnappedToPortAndMouseUp()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(0, 0, 1000, 400));
+ diagram.Options.Links.EnableSnapping = true;
+ diagram.Options.Links.SnappingRadius = 60;
+ var node1 = new NodeModel(position: new Point(100, 50));
+ var node2 = new NodeModel(position: new Point(160, 50));
+ diagram.Nodes.Add(new[] { node1, node2 });
+ var port1 = node1.AddPort(new PortModel(node1)
+ {
+ Initialized = true,
+ Position = new Point(110, 60),
+ Size = new Size(10, 20)
+ });
+ var port2 = node2.AddPort(new PortModel(node2)
+ {
+ Initialized = true,
+ Position = new Point(170, 60),
+ Size = new Size(10, 20)
+ });
+ var targetAttachedTriggers = 0;
+
+ // Act
+ diagram.TriggerPointerDown(port1,
+ new PointerEventArgs(100, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ diagram.TriggerPointerMove(null,
+ new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ diagram.Links.Single().TargetAttached += _ => targetAttachedTriggers++;
+
+ diagram.TriggerPointerUp(null,
+ new PointerEventArgs(140, 100, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ targetAttachedTriggers.Should().Be(1);
}
}
\ No newline at end of file
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/EventsBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/EventsBehaviorTests.cs
index 1c6d16738..5df0724d8 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Behaviors/EventsBehaviorTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/EventsBehaviorTests.cs
@@ -4,106 +4,105 @@
using System.Threading.Tasks;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Behaviors
+namespace Blazor.Diagrams.Core.Tests.Behaviors;
+
+public class EventsBehaviorTests
{
- public class EventsBehaviorTests
+ [Fact]
+ public void Behavior_ShouldNotTriggerMouseClick_WhenItsRemoved()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.UnregisterBehavior();
+ var eventTriggered = false;
+
+ // Act
+ diagram.PointerClick += (m, e) => eventTriggered = true;
+ diagram.TriggerPointerDown(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ eventTriggered.Should().BeFalse();
+ }
+
+ [Fact]
+ public void Behavior_ShouldTriggerMouseClick_WhenMouseDownThenUpWithoutMove()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var eventTriggered = false;
+
+ // Act
+ diagram.PointerClick += (m, e) => eventTriggered = true;
+ diagram.TriggerPointerDown(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ eventTriggered.Should().BeTrue();
+ }
+
+ [Fact]
+ public void Behavior_ShouldNotTriggerMouseClick_WhenMouseMoves()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var eventTriggered = false;
+
+ // Act
+ diagram.PointerClick += (m, e) => eventTriggered = true;
+ diagram.TriggerPointerDown(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerMove(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ eventTriggered.Should().BeFalse();
+ }
+
+ [Fact]
+ public void Behavior_ShouldTriggerMouseDoubleClick_WhenTwoMouseClicksHappenWithinTime()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var eventTriggered = false;
+
+ // Act
+ diagram.PointerDoubleClick += (m, e) => eventTriggered = true;
+ diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ eventTriggered.Should().BeTrue();
+ }
+
+ [Fact]
+ public async Task Behavior_ShouldNotTriggerMouseDoubleClick_WhenTimeExceeds500()
{
- [Fact]
- public void Behavior_ShouldNotTriggerMouseClick_WhenItsRemoved()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.UnregisterBehavior();
- var eventTriggered = false;
-
- // Act
- diagram.PointerClick += (m, e) => eventTriggered = true;
- diagram.TriggerPointerDown(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- eventTriggered.Should().BeFalse();
- }
-
- [Fact]
- public void Behavior_ShouldTriggerMouseClick_WhenMouseDownThenUpWithoutMove()
- {
- // Arrange
- var diagram = new TestDiagram();
- var eventTriggered = false;
-
- // Act
- diagram.PointerClick += (m, e) => eventTriggered = true;
- diagram.TriggerPointerDown(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- eventTriggered.Should().BeTrue();
- }
-
- [Fact]
- public void Behavior_ShouldNotTriggerMouseClick_WhenMouseMoves()
- {
- // Arrange
- var diagram = new TestDiagram();
- var eventTriggered = false;
-
- // Act
- diagram.PointerClick += (m, e) => eventTriggered = true;
- diagram.TriggerPointerDown(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerMove(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- eventTriggered.Should().BeFalse();
- }
-
- [Fact]
- public void Behavior_ShouldTriggerMouseDoubleClick_WhenTwoMouseClicksHappenWithinTime()
- {
- // Arrange
- var diagram = new TestDiagram();
- var eventTriggered = false;
-
- // Act
- diagram.PointerDoubleClick += (m, e) => eventTriggered = true;
- diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- eventTriggered.Should().BeTrue();
- }
-
- [Fact]
- public async Task Behavior_ShouldNotTriggerMouseDoubleClick_WhenTimeExceeds500()
- {
- // Arrange
- var diagram = new TestDiagram();
- var eventTriggered = false;
-
- // Act
- diagram.PointerDoubleClick += (m, e) => eventTriggered = true;
- diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
- await Task.Delay(520);
- diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- eventTriggered.Should().BeFalse();
- }
-
- [Fact]
- public void Behavior_ShouldTriggerMouseClick_OnlyWhenMouseDownWasAlsoTriggered_Issue204()
- {
- // Arrange
- var diagram = new TestDiagram();
- var eventTriggered = false;
-
- // Act
- diagram.PointerClick += (m, e) => eventTriggered = true;
- diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
-
- // Assert
- eventTriggered.Should().BeFalse();
- }
+ // Arrange
+ var diagram = new TestDiagram();
+ var eventTriggered = false;
+
+ // Act
+ diagram.PointerDoubleClick += (m, e) => eventTriggered = true;
+ diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+ await Task.Delay(520);
+ diagram.TriggerPointerClick(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ eventTriggered.Should().BeFalse();
+ }
+
+ [Fact]
+ public void Behavior_ShouldTriggerMouseClick_OnlyWhenMouseDownWasAlsoTriggered_Issue204()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var eventTriggered = false;
+
+ // Act
+ diagram.PointerClick += (m, e) => eventTriggered = true;
+ diagram.TriggerPointerUp(null, new PointerEventArgs(0, 0, 0, 0, false, false, false, 0, 0, 0, 0, 0, 0, string.Empty, true));
+
+ // Assert
+ eventTriggered.Should().BeFalse();
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsBehaviorTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsBehaviorTests.cs
index 0bc9f3c0e..42fe887b7 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsBehaviorTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsBehaviorTests.cs
@@ -4,87 +4,86 @@
using System.Threading.Tasks;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Behaviors
+namespace Blazor.Diagrams.Core.Tests.Behaviors;
+
+public class KeyboardShortcutsBehaviorTests
{
- public class KeyboardShortcutsBehaviorTests
+ [Theory]
+ [InlineData("A", true, true, true)]
+ [InlineData("B", true, false, false)]
+ [InlineData("C", true, true, false)]
+ [InlineData("D", true, false, true)]
+ [InlineData("E", false, false, true)]
+ [InlineData("F", false, true, true)]
+ [InlineData("G", false, false, false)]
+ public void Behavior_ShouldExecuteAction_WhenCombinationIsPressed(string key, bool ctrl, bool shift, bool alt)
{
- [Theory]
- [InlineData("A", true, true, true)]
- [InlineData("B", true, false, false)]
- [InlineData("C", true, true, false)]
- [InlineData("D", true, false, true)]
- [InlineData("E", false, false, true)]
- [InlineData("F", false, true, true)]
- [InlineData("G", false, false, false)]
- public void Behavior_ShouldExecuteAction_WhenCombinationIsPressed(string key, bool ctrl, bool shift, bool alt)
+ // Arrange
+ var diagram = new TestDiagram();
+ var ksb = diagram.GetBehavior()!;
+ var executed = false;
+
+ ksb.SetShortcut(key, ctrl, shift, alt, d =>
{
- // Arrange
- var diagram = new TestDiagram();
- var ksb = diagram.GetBehavior()!;
- var executed = false;
+ executed = true;
+ return ValueTask.CompletedTask;
+ });
- ksb.SetShortcut(key, ctrl, shift, alt, d =>
- {
- executed = true;
- return ValueTask.CompletedTask;
- });
+ // Act
+ diagram.TriggerKeyDown(new KeyboardEventArgs(key, key, 0, ctrl, shift, alt));
- // Act
- diagram.TriggerKeyDown(new KeyboardEventArgs(key, key, 0, ctrl, shift, alt));
+ // Assert
+ executed.Should().BeTrue();
+ }
- // Assert
- executed.Should().BeTrue();
- }
+ [Fact]
+ public void Behavior_ShouldDoNothing_WhenRemoved()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var ksb = diagram.GetBehavior()!;
+ diagram.UnregisterBehavior();
+ var executed = false;
- [Fact]
- public void Behavior_ShouldDoNothing_WhenRemoved()
+ ksb.SetShortcut("A", false, false, false, d =>
{
- // Arrange
- var diagram = new TestDiagram();
- var ksb = diagram.GetBehavior()!;
- diagram.UnregisterBehavior();
- var executed = false;
+ executed = true;
+ return ValueTask.CompletedTask;
+ });
- ksb.SetShortcut("A", false, false, false, d =>
- {
- executed = true;
- return ValueTask.CompletedTask;
- });
+ // Act
+ diagram.TriggerKeyDown(new KeyboardEventArgs("A", "A", 0, false, false, false));
- // Act
- diagram.TriggerKeyDown(new KeyboardEventArgs("A", "A", 0, false, false, false));
+ // Assert
+ executed.Should().BeFalse();
+ }
- // Assert
- executed.Should().BeFalse();
- }
+ [Fact]
+ public void SetShortcut_ShouldOverride()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var ksb = diagram.GetBehavior()!;
+ var executed1 = false;
+ var executed2 = false;
- [Fact]
- public void SetShortcut_ShouldOverride()
+ ksb.SetShortcut("A", false, false, false, d =>
{
- // Arrange
- var diagram = new TestDiagram();
- var ksb = diagram.GetBehavior()!;
- var executed1 = false;
- var executed2 = false;
+ executed1 = true;
+ return ValueTask.CompletedTask;
+ });
- ksb.SetShortcut("A", false, false, false, d =>
- {
- executed1 = true;
- return ValueTask.CompletedTask;
- });
-
- ksb.SetShortcut("A", false, false, false, d =>
- {
- executed2 = true;
- return ValueTask.CompletedTask;
- });
+ ksb.SetShortcut("A", false, false, false, d =>
+ {
+ executed2 = true;
+ return ValueTask.CompletedTask;
+ });
- // Act
- diagram.TriggerKeyDown(new KeyboardEventArgs("A", "A", 0, false, false, false));
+ // Act
+ diagram.TriggerKeyDown(new KeyboardEventArgs("A", "A", 0, false, false, false));
- // Assert
- executed1.Should().BeFalse();
- executed2.Should().BeTrue();
- }
+ // Assert
+ executed1.Should().BeFalse();
+ executed2.Should().BeTrue();
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsDefaultsTests.cs b/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsDefaultsTests.cs
index 9a1839c52..3a30fc040 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsDefaultsTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Behaviors/KeyboardShortcutsDefaultsTests.cs
@@ -5,130 +5,129 @@
using System.Threading.Tasks;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Behaviors
+namespace Blazor.Diagrams.Core.Tests.Behaviors;
+
+public class KeyboardShortcutsDefaultsTests
{
- public class KeyboardShortcutsDefaultsTests
+ [Fact]
+ public async Task DeleteSelection_ShouldNotDeleteModel_WhenItsLocked()
{
- [Fact]
- public async Task DeleteSelection_ShouldNotDeleteModel_WhenItsLocked()
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.Nodes.Add(new NodeModel
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.Nodes.Add(new NodeModel
- {
- Selected = true,
- Locked = true
- });
-
- // Act
- await KeyboardShortcutsDefaults.DeleteSelection(diagram);
-
- // Assert
- diagram.Nodes.Count.Should().Be(1);
- }
-
- [Fact]
- public async Task DeleteSelection_ShouldTakeIntoAccountGroupConstraint()
+ Selected = true,
+ Locked = true
+ });
+
+ // Act
+ await KeyboardShortcutsDefaults.DeleteSelection(diagram);
+
+ // Assert
+ diagram.Nodes.Count.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task DeleteSelection_ShouldTakeIntoAccountGroupConstraint()
+ {
+ // Arrange
+ var funcCalled = false;
+ var diagram = new TestDiagram();
+ diagram.Options.Constraints.ShouldDeleteGroup = _ =>
{
- // Arrange
- var funcCalled = false;
- var diagram = new TestDiagram();
- diagram.Options.Constraints.ShouldDeleteGroup = _ =>
- {
- funcCalled = true;
- return ValueTask.FromResult(false);
- };
- diagram.Groups.Add(new GroupModel(Array.Empty())
- {
- Selected = true
- });
-
- // Act
- await KeyboardShortcutsDefaults.DeleteSelection(diagram);
-
- // Assert
- funcCalled.Should().BeTrue();
- diagram.Groups.Count.Should().Be(1);
- }
-
- [Fact]
- public async Task DeleteSelection_ShouldTakeIntoAccountNodeConstraint()
+ funcCalled = true;
+ return ValueTask.FromResult(false);
+ };
+ diagram.Groups.Add(new GroupModel(Array.Empty())
{
- // Arrange
- var funcCalled = false;
- var diagram = new TestDiagram();
- diagram.Options.Constraints.ShouldDeleteNode = _ =>
- {
- funcCalled = true;
- return ValueTask.FromResult(false);
- };
- diagram.Nodes.Add(new NodeModel
- {
- Selected = true
- });
-
- // Act
- await KeyboardShortcutsDefaults.DeleteSelection(diagram);
-
- // Assert
- funcCalled.Should().BeTrue();
- diagram.Nodes.Count.Should().Be(1);
- }
-
- [Fact]
- public async Task DeleteSelection_ShouldTakeIntoAccountLinkConstraint()
+ Selected = true
+ });
+
+ // Act
+ await KeyboardShortcutsDefaults.DeleteSelection(diagram);
+
+ // Assert
+ funcCalled.Should().BeTrue();
+ diagram.Groups.Count.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task DeleteSelection_ShouldTakeIntoAccountNodeConstraint()
+ {
+ // Arrange
+ var funcCalled = false;
+ var diagram = new TestDiagram();
+ diagram.Options.Constraints.ShouldDeleteNode = _ =>
{
- // Arrange
- var funcCalled = false;
- var diagram = new TestDiagram();
- diagram.Options.Constraints.ShouldDeleteLink = _ =>
- {
- funcCalled = true;
- return ValueTask.FromResult(false);
- };
- diagram.Nodes.Add(new NodeModel[]
- {
- new NodeModel(),
- new NodeModel()
- });
- diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Nodes[1])
- {
- Selected = true
- });
-
- // Act
- await KeyboardShortcutsDefaults.DeleteSelection(diagram);
-
- // Assert
- funcCalled.Should().BeTrue();
- diagram.Links.Count.Should().Be(1);
- }
-
- [Fact]
- public async Task DeleteSelection_ShouldResultInSingleRefresh()
+ funcCalled = true;
+ return ValueTask.FromResult(false);
+ };
+ diagram.Nodes.Add(new NodeModel
{
- // Arrange
- var diagram = new TestDiagram();
- diagram.Nodes.Add(new NodeModel[]
- {
- new NodeModel { Selected = true },
- new NodeModel { Selected = true }
- });
- diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Nodes[1])
- {
- Selected = true
- });
-
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
-
- // Act
- await KeyboardShortcutsDefaults.DeleteSelection(diagram);
-
- // Assert
- diagram.Nodes.Count.Should().Be(0);
- diagram.Links.Count.Should().Be(0);
- refreshes.Should().Be(1);
- }
+ Selected = true
+ });
+
+ // Act
+ await KeyboardShortcutsDefaults.DeleteSelection(diagram);
+
+ // Assert
+ funcCalled.Should().BeTrue();
+ diagram.Nodes.Count.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task DeleteSelection_ShouldTakeIntoAccountLinkConstraint()
+ {
+ // Arrange
+ var funcCalled = false;
+ var diagram = new TestDiagram();
+ diagram.Options.Constraints.ShouldDeleteLink = _ =>
+ {
+ funcCalled = true;
+ return ValueTask.FromResult(false);
+ };
+ diagram.Nodes.Add(new NodeModel[]
+ {
+ new NodeModel(),
+ new NodeModel()
+ });
+ diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Nodes[1])
+ {
+ Selected = true
+ });
+
+ // Act
+ await KeyboardShortcutsDefaults.DeleteSelection(diagram);
+
+ // Assert
+ funcCalled.Should().BeTrue();
+ diagram.Links.Count.Should().Be(1);
+ }
+
+ [Fact]
+ public async Task DeleteSelection_ShouldResultInSingleRefresh()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.Nodes.Add(new NodeModel[]
+ {
+ new NodeModel { Selected = true },
+ new NodeModel { Selected = true }
+ });
+ diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Nodes[1])
+ {
+ Selected = true
+ });
+
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
+
+ // Act
+ await KeyboardShortcutsDefaults.DeleteSelection(diagram);
+
+ // Assert
+ diagram.Nodes.Count.Should().Be(0);
+ diagram.Links.Count.Should().Be(0);
+ refreshes.Should().Be(1);
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/DiagramOrderingTests.cs b/tests/Blazor.Diagrams.Core.Tests/DiagramOrderingTests.cs
index 492f139b8..5493e51fa 100644
--- a/tests/Blazor.Diagrams.Core.Tests/DiagramOrderingTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/DiagramOrderingTests.cs
@@ -3,196 +3,195 @@
using System;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests
+namespace Blazor.Diagrams.Core.Tests;
+
+public class DiagramOrderingTests
{
- public class DiagramOrderingTests
+ [Fact]
+ public void GetMinOrder_ShouldReturnZeroWhenNoModelsHaveBeenAdded()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+
+ // Act
+ var minOrder = diagram.GetMinOrder();
+
+ // Assert
+ minOrder.Should().Be(0);
+ }
+
+ [Fact]
+ public void GetMinOrder_ShouldReturnCorrectValue()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.Nodes.Add(new NodeModel());
+ diagram.Groups.Add(new GroupModel(Array.Empty()));
+ diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Groups[0]));
+
+ // Act
+ var minOrder = diagram.GetMinOrder();
+
+ // Assert
+ minOrder.Should().Be(1);
+ }
+
+ [Fact]
+ public void GetMaxOrder_ShouldReturnZeroWhenNoModelsHaveBeenAdded()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+
+ // Act
+ var maxOrder = diagram.GetMaxOrder();
+
+ // Assert
+ maxOrder.Should().Be(0);
+ }
+
+ [Fact]
+ public void GetMaxOrder_ShouldReturnCorrectValue()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.Nodes.Add(new NodeModel());
+ diagram.Groups.Add(new GroupModel(Array.Empty()));
+ diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Groups[0]));
+
+ // Act
+ var maxOrder = diagram.GetMaxOrder();
+
+ // Assert
+ maxOrder.Should().Be(3);
+ }
+
+ [Fact]
+ public void Diagram_ShouldReSortWhenModelOrderChanges()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel()); // 1
+ var node2 = diagram.Nodes.Add(new NodeModel()); // 2
+
+ // Act
+ node1.Order = 10;
+
+ // Assert
+ diagram.OrderedSelectables[0].Should().Be(node2);
+ diagram.OrderedSelectables[1].Should().Be(node1);
+ }
+
+ [Fact]
+ public void Diagram_ShouldRefreshOnceWhenModelOrderChanges()
{
- [Fact]
- public void GetMinOrder_ShouldReturnZeroWhenNoModelsHaveBeenAdded()
- {
- // Arrange
- var diagram = new TestDiagram();
-
- // Act
- var minOrder = diagram.GetMinOrder();
-
- // Assert
- minOrder.Should().Be(0);
- }
-
- [Fact]
- public void GetMinOrder_ShouldReturnCorrectValue()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.Nodes.Add(new NodeModel());
- diagram.Groups.Add(new GroupModel(Array.Empty()));
- diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Groups[0]));
-
- // Act
- var minOrder = diagram.GetMinOrder();
-
- // Assert
- minOrder.Should().Be(1);
- }
-
- [Fact]
- public void GetMaxOrder_ShouldReturnZeroWhenNoModelsHaveBeenAdded()
- {
- // Arrange
- var diagram = new TestDiagram();
-
- // Act
- var maxOrder = diagram.GetMaxOrder();
-
- // Assert
- maxOrder.Should().Be(0);
- }
-
- [Fact]
- public void GetMaxOrder_ShouldReturnCorrectValue()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.Nodes.Add(new NodeModel());
- diagram.Groups.Add(new GroupModel(Array.Empty()));
- diagram.Links.Add(new LinkModel(diagram.Nodes[0], diagram.Groups[0]));
-
- // Act
- var maxOrder = diagram.GetMaxOrder();
-
- // Assert
- maxOrder.Should().Be(3);
- }
-
- [Fact]
- public void Diagram_ShouldReSortWhenModelOrderChanges()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel()); // 1
- var node2 = diagram.Nodes.Add(new NodeModel()); // 2
-
- // Act
- node1.Order = 10;
-
- // Assert
- diagram.OrderedSelectables[0].Should().Be(node2);
- diagram.OrderedSelectables[1].Should().Be(node1);
- }
-
- [Fact]
- public void Diagram_ShouldRefreshOnceWhenModelOrderChanges()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel()); // 1
- var node2 = diagram.Nodes.Add(new NodeModel()); // 2
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
-
- // Act
- node1.Order = 10;
-
- // Assert
- refreshes.Should().Be(1);
- }
-
- [Fact]
- public void SendToBack_ShouldInsertAtZeroAndFixOrders()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel());
- var node2 = diagram.Nodes.Add(new NodeModel());
- var node3 = diagram.Nodes.Add(new NodeModel());
-
- // Act
- diagram.SendToBack(node3);
-
- // Assert
- diagram.OrderedSelectables[0].Should().Be(node3);
- diagram.OrderedSelectables[0].Order.Should().Be(1);
-
- diagram.OrderedSelectables[1].Should().Be(node1);
- diagram.OrderedSelectables[1].Order.Should().Be(2);
-
- diagram.OrderedSelectables[2].Should().Be(node2);
- diagram.OrderedSelectables[2].Order.Should().Be(3);
- }
-
- [Fact]
- public void SendToFront_ShouldAddAndFixOrder()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel());
- var node2 = diagram.Nodes.Add(new NodeModel());
- var node3 = diagram.Nodes.Add(new NodeModel());
-
- // Act
- diagram.SendToFront(node1);
-
- // Assert
- diagram.OrderedSelectables[0].Should().Be(node2);
- diagram.OrderedSelectables[0].Order.Should().Be(2);
-
- diagram.OrderedSelectables[1].Should().Be(node3);
- diagram.OrderedSelectables[1].Order.Should().Be(3);
-
- diagram.OrderedSelectables[2].Should().Be(node1);
- diagram.OrderedSelectables[2].Order.Should().Be(4);
- }
-
- [Fact]
- public void Diagram_ShouldRefreshOnceWhenMultipleModelsWereRemoved()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel());
- var node2 = diagram.Nodes.Add(new NodeModel());
- var link = diagram.Links.Add(new LinkModel(node1, node2));
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
-
- // Act
- diagram.Nodes.Remove(node1);
-
- // Assert
- refreshes.Should().Be(1);
- }
-
- [Fact]
- public void Diagram_ShouldNotUpdateOrders_WhenSuspendSortingIsTrue()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.SuspendSorting = true;
- var node1 = diagram.Nodes.Add(new NodeModel()); // 1
- var node2 = diagram.Nodes.Add(new NodeModel()); // 2
-
- // Act
- node1.Order = 10;
-
- // Assert
- diagram.OrderedSelectables[0].Should().Be(node1);
- diagram.OrderedSelectables[1].Should().Be(node2);
- }
-
- [Fact]
- public void RefreshOrders_ShouldSortModels()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel() { Order = 10 });
- var node2 = diagram.Nodes.Add(new NodeModel() { Order = 5 });
-
- // Act
- diagram.RefreshOrders();
-
- // Assert
- diagram.OrderedSelectables[0].Should().Be(node2);
- diagram.OrderedSelectables[1].Should().Be(node1);
- }
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel()); // 1
+ var node2 = diagram.Nodes.Add(new NodeModel()); // 2
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
+
+ // Act
+ node1.Order = 10;
+
+ // Assert
+ refreshes.Should().Be(1);
+ }
+
+ [Fact]
+ public void SendToBack_ShouldInsertAtZeroAndFixOrders()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel());
+ var node2 = diagram.Nodes.Add(new NodeModel());
+ var node3 = diagram.Nodes.Add(new NodeModel());
+
+ // Act
+ diagram.SendToBack(node3);
+
+ // Assert
+ diagram.OrderedSelectables[0].Should().Be(node3);
+ diagram.OrderedSelectables[0].Order.Should().Be(1);
+
+ diagram.OrderedSelectables[1].Should().Be(node1);
+ diagram.OrderedSelectables[1].Order.Should().Be(2);
+
+ diagram.OrderedSelectables[2].Should().Be(node2);
+ diagram.OrderedSelectables[2].Order.Should().Be(3);
+ }
+
+ [Fact]
+ public void SendToFront_ShouldAddAndFixOrder()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel());
+ var node2 = diagram.Nodes.Add(new NodeModel());
+ var node3 = diagram.Nodes.Add(new NodeModel());
+
+ // Act
+ diagram.SendToFront(node1);
+
+ // Assert
+ diagram.OrderedSelectables[0].Should().Be(node2);
+ diagram.OrderedSelectables[0].Order.Should().Be(2);
+
+ diagram.OrderedSelectables[1].Should().Be(node3);
+ diagram.OrderedSelectables[1].Order.Should().Be(3);
+
+ diagram.OrderedSelectables[2].Should().Be(node1);
+ diagram.OrderedSelectables[2].Order.Should().Be(4);
+ }
+
+ [Fact]
+ public void Diagram_ShouldRefreshOnceWhenMultipleModelsWereRemoved()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel());
+ var node2 = diagram.Nodes.Add(new NodeModel());
+ var link = diagram.Links.Add(new LinkModel(node1, node2));
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
+
+ // Act
+ diagram.Nodes.Remove(node1);
+
+ // Assert
+ refreshes.Should().Be(1);
+ }
+
+ [Fact]
+ public void Diagram_ShouldNotUpdateOrders_WhenSuspendSortingIsTrue()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SuspendSorting = true;
+ var node1 = diagram.Nodes.Add(new NodeModel()); // 1
+ var node2 = diagram.Nodes.Add(new NodeModel()); // 2
+
+ // Act
+ node1.Order = 10;
+
+ // Assert
+ diagram.OrderedSelectables[0].Should().Be(node1);
+ diagram.OrderedSelectables[1].Should().Be(node2);
+ }
+
+ [Fact]
+ public void RefreshOrders_ShouldSortModels()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel() { Order = 10 });
+ var node2 = diagram.Nodes.Add(new NodeModel() { Order = 5 });
+
+ // Act
+ diagram.RefreshOrders();
+
+ // Assert
+ diagram.OrderedSelectables[0].Should().Be(node2);
+ diagram.OrderedSelectables[1].Should().Be(node1);
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/DiagramTests.cs b/tests/Blazor.Diagrams.Core.Tests/DiagramTests.cs
index b8e7d4da3..85a812dfc 100644
--- a/tests/Blazor.Diagrams.Core.Tests/DiagramTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/DiagramTests.cs
@@ -4,123 +4,122 @@
using System;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests
+namespace Blazor.Diagrams.Core.Tests;
+
+public class DiagramTests
{
- public class DiagramTests
+ [Fact]
+ public void GetScreenPoint_ShouldReturnCorrectPoint()
{
- [Fact]
- public void GetScreenPoint_ShouldReturnCorrectPoint()
- {
- // Arrange
- var diagram = new TestDiagram();
-
- // Act
- diagram.SetZoom(1.234);
- diagram.UpdatePan(50, 50);
- diagram.SetContainer(new Rectangle(30, 65, 1000, 793));
- var pt = diagram.GetScreenPoint(100, 200);
-
- // Assert
- pt.X.Should().Be(203.4); // 2*X + panX + left
- pt.Y.Should().Be(361.8); // 2*Y + panY + top
- }
-
- [Fact]
- public void ZoomToFit_ShouldUseSelectedNodesIfAny()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(1080, 768)));
- diagram.Nodes.Add(new NodeModel(new Point(50, 50))
- {
- Size = new Size(100, 80)
- });
- diagram.SelectModel(diagram.Nodes[0], true);
-
- // Act
- diagram.ZoomToFit(10);
-
- // Assert
- diagram.Zoom.Should().BeApproximately(7.68, 0.001);
- diagram.Pan.X.Should().Be(-307.2);
- diagram.Pan.Y.Should().Be(-307.2);
- }
-
- [Fact]
- public void ZoomToFit_ShouldUseNodesWhenNoneSelected()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(1080, 768)));
- diagram.Nodes.Add(new NodeModel(new Point(50, 50))
- {
- Size = new Size(100, 80)
- });
-
- // Act
- diagram.ZoomToFit(10);
-
- // Assert
- diagram.Zoom.Should().BeApproximately(7.68, 0.001);
- diagram.Pan.X.Should().Be(-307.2);
- diagram.Pan.Y.Should().Be(-307.2);
- }
-
- [Fact]
- public void ZoomToFit_ShouldTriggerAppropriateEvents()
- {
- // Arrange
- var diagram = new TestDiagram();
- diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(1080, 768)));
- diagram.Nodes.Add(new NodeModel(new Point(50, 50))
- {
- Size = new Size(100, 80)
- });
-
- var refreshes = 0;
- var zoomChanges = 0;
- var panChanges = 0;
-
- // Act
- diagram.Changed += () => refreshes++;
- diagram.ZoomChanged += () => zoomChanges++;
- diagram.PanChanged += () => panChanges++;
- diagram.ZoomToFit(10);
-
- // Assert
- refreshes.Should().Be(1);
- zoomChanges.Should().Be(1);
- panChanges.Should().Be(1);
- }
-
- [Theory]
- [InlineData(0.001)]
- [InlineData(0.1)]
- public void Zoom_ShoulClampToMinimumValue(double zoomValue)
+ // Arrange
+ var diagram = new TestDiagram();
+
+ // Act
+ diagram.SetZoom(1.234);
+ diagram.UpdatePan(50, 50);
+ diagram.SetContainer(new Rectangle(30, 65, 1000, 793));
+ var pt = diagram.GetScreenPoint(100, 200);
+
+ // Assert
+ pt.X.Should().Be(203.4); // 2*X + panX + left
+ pt.Y.Should().Be(361.8); // 2*Y + panY + top
+ }
+
+ [Fact]
+ public void ZoomToFit_ShouldUseSelectedNodesIfAny()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(1080, 768)));
+ diagram.Nodes.Add(new NodeModel(new Point(50, 50))
{
- var diagram = new TestDiagram();
- diagram.SetZoom(zoomValue);
- Assert.Equal(diagram.Zoom, diagram.Options.Zoom.Minimum);
- }
-
- [Theory]
- [InlineData(0)]
- [InlineData(-0.1)]
- [InlineData(-0.00001)]
- public void Zoom_ThrowExceptionWhenLessThan0(double zoomValue)
+ Size = new Size(100, 80)
+ });
+ diagram.SelectModel(diagram.Nodes[0], true);
+
+ // Act
+ diagram.ZoomToFit(10);
+
+ // Assert
+ diagram.Zoom.Should().BeApproximately(7.68, 0.001);
+ diagram.Pan.X.Should().Be(-307.2);
+ diagram.Pan.Y.Should().Be(-307.2);
+ }
+
+ [Fact]
+ public void ZoomToFit_ShouldUseNodesWhenNoneSelected()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(1080, 768)));
+ diagram.Nodes.Add(new NodeModel(new Point(50, 50))
{
- var diagram = new TestDiagram();
- Assert.Throws(() => diagram.SetZoom(zoomValue));
- }
-
- [Theory]
- [InlineData(0)]
- [InlineData(-0.1)]
- [InlineData(-0.00001)]
- public void ZoomOptions_ThrowExceptionWhenLessThan0(double zoomValue)
+ Size = new Size(100, 80)
+ });
+
+ // Act
+ diagram.ZoomToFit(10);
+
+ // Assert
+ diagram.Zoom.Should().BeApproximately(7.68, 0.001);
+ diagram.Pan.X.Should().Be(-307.2);
+ diagram.Pan.Y.Should().Be(-307.2);
+ }
+
+ [Fact]
+ public void ZoomToFit_ShouldTriggerAppropriateEvents()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ diagram.SetContainer(new Rectangle(new Point(0, 0), new Size(1080, 768)));
+ diagram.Nodes.Add(new NodeModel(new Point(50, 50))
{
- var diagram = new TestDiagram();
- Assert.Throws(() => diagram.Options.Zoom.Minimum = zoomValue);
- }
+ Size = new Size(100, 80)
+ });
+
+ var refreshes = 0;
+ var zoomChanges = 0;
+ var panChanges = 0;
+
+ // Act
+ diagram.Changed += () => refreshes++;
+ diagram.ZoomChanged += () => zoomChanges++;
+ diagram.PanChanged += () => panChanges++;
+ diagram.ZoomToFit(10);
+
+ // Assert
+ refreshes.Should().Be(1);
+ zoomChanges.Should().Be(1);
+ panChanges.Should().Be(1);
+ }
+
+ [Theory]
+ [InlineData(0.001)]
+ [InlineData(0.1)]
+ public void Zoom_ShoulClampToMinimumValue(double zoomValue)
+ {
+ var diagram = new TestDiagram();
+ diagram.SetZoom(zoomValue);
+ Assert.Equal(diagram.Zoom, diagram.Options.Zoom.Minimum);
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(-0.1)]
+ [InlineData(-0.00001)]
+ public void Zoom_ThrowExceptionWhenLessThan0(double zoomValue)
+ {
+ var diagram = new TestDiagram();
+ Assert.Throws(() => diagram.SetZoom(zoomValue));
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(-0.1)]
+ [InlineData(-0.00001)]
+ public void ZoomOptions_ThrowExceptionWhenLessThan0(double zoomValue)
+ {
+ var diagram = new TestDiagram();
+ Assert.Throws(() => diagram.Options.Zoom.Minimum = zoomValue);
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs b/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs
index 3ecc98a65..534790cfc 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Extensions/DiagramExtensionsTests.cs
@@ -3,47 +3,46 @@
using Blazor.Diagrams.Core.Models;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Extensions
+namespace Blazor.Diagrams.Core.Tests.Extensions;
+
+public class DiagramExtensionsTests
{
- public class DiagramExtensionsTests
+ [Fact]
+ public void GetBounds_ShouldReturnZeroRectangle_WhenNodesAreEmpty()
{
- [Fact]
- public void GetBounds_ShouldReturnZeroRectangle_WhenNodesAreEmpty()
- {
- // Arrange
- var nodes = new NodeModel[0];
+ // Arrange
+ var nodes = new NodeModel[0];
- // Act
- var bounds = nodes.GetBounds();
+ // Act
+ var bounds = nodes.GetBounds();
- // Assert
- Assert.True(Rectangle.Zero.Equals(bounds));
- }
+ // Assert
+ Assert.True(Rectangle.Zero.Equals(bounds));
+ }
- [Fact]
- public void GetBounds_ShouldReturnCorrectBounds()
+ [Fact]
+ public void GetBounds_ShouldReturnCorrectBounds()
+ {
+ // Arrange
+ var nodes = new NodeModel[]
{
- // Arrange
- var nodes = new NodeModel[]
+ new NodeModel
+ {
+ Position = new Point(10, 10),
+ Size = new Size(100, 100)
+ },
+ 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)
- },
- };
+ Position = new Point(200, 200),
+ Size = new Size(100, 100)
+ },
+ };
- // Act
- var bounds = nodes.GetBounds();
+ // Act
+ var bounds = nodes.GetBounds();
- // Assert
- var expected = new Rectangle(10, 10, 300, 300);
- Assert.True(expected.Equals(bounds));
- }
+ // 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
index c454a415c..d2bc5bbf9 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Extensions/DoubleExtensionsTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Extensions/DoubleExtensionsTests.cs
@@ -1,21 +1,20 @@
using Xunit;
using Blazor.Diagrams.Core.Extensions;
-namespace Blazor.Diagrams.Core.Tests.Extensions
+namespace Blazor.Diagrams.Core.Tests.Extensions;
+
+public class DoubleExtensionsTests
{
- 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)
{
- [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));
- }
+ Assert.Equal(expected, num1.AlmostEqualTo(num2, tolerance));
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs b/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs
index ab15e59a8..104103578 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Geometry/PointTests.cs
@@ -2,20 +2,19 @@
using FluentAssertions;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Geometry
+namespace Blazor.Diagrams.Core.Tests.Geometry;
+
+public class PointTests
{
- 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)
{
- [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);
- }
+ var pt1 = new Point(x1, y1);
+ var pt2 = new Point(x2, y2);
+ pt1.DistanceTo(pt2).Should().BeApproximately(expected, 0.0001);
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Layers/GroupLayerTests.cs b/tests/Blazor.Diagrams.Core.Tests/Layers/GroupLayerTests.cs
index 877d33cc0..c17398c6b 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Layers/GroupLayerTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Layers/GroupLayerTests.cs
@@ -3,173 +3,172 @@
using System;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Layers
+namespace Blazor.Diagrams.Core.Tests.Layers;
+
+public class GroupLayerTests
{
- public class GroupLayerTests
+ [Fact]
+ public void Group_ShouldCallFactoryThenAddMethod()
{
- [Fact]
- public void Group_ShouldCallFactoryThenAddMethod()
+ // Arrange
+ var diagram = new TestDiagram();
+ var factoryCalled = false;
+
+ diagram.Options.Groups.Factory = (_, children) =>
{
- // Arrange
- var diagram = new TestDiagram();
- var factoryCalled = false;
+ factoryCalled = true;
+ return new GroupModel(children);
+ };
- diagram.Options.Groups.Factory = (_, children) =>
- {
- factoryCalled = true;
- return new GroupModel(children);
- };
+ // Act
+ diagram.Groups.Group(Array.Empty());
- // Act
- diagram.Groups.Group(Array.Empty());
+ // Assert
+ factoryCalled.Should().BeTrue();
+ }
- // Assert
- factoryCalled.Should().BeTrue();
- }
+ [Fact]
+ public void Remove_ShouldRemoveAllPortLinks()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
+ var groupPort = group.AddPort(PortAlignment.Top);
+ var node = diagram.Nodes.Add(new NodeModel());
+ var nodePort = node.AddPort(PortAlignment.Top);
+ diagram.Links.Add(new LinkModel(groupPort, nodePort));
+
+ // Act
+ diagram.Groups.Remove(group);
+
+ // Assert
+ diagram.Links.Should().BeEmpty();
+ }
- [Fact]
- public void Remove_ShouldRemoveAllPortLinks()
- {
- // Arrange
- var diagram = new TestDiagram();
- var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
- var groupPort = group.AddPort(PortAlignment.Top);
- var node = diagram.Nodes.Add(new NodeModel());
- var nodePort = node.AddPort(PortAlignment.Top);
- diagram.Links.Add(new LinkModel(groupPort, nodePort));
-
- // Act
- diagram.Groups.Remove(group);
-
- // Assert
- diagram.Links.Should().BeEmpty();
- }
-
- [Fact]
- public void Remove_ShouldRemoveAllLinks()
- {
- // Arrange
- var diagram = new TestDiagram();
- var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
- var node = diagram.Nodes.Add(new NodeModel());
- diagram.Links.Add(new LinkModel(group, node));
+ [Fact]
+ public void Remove_ShouldRemoveAllLinks()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
+ var node = diagram.Nodes.Add(new NodeModel());
+ diagram.Links.Add(new LinkModel(group, node));
- // Act
- diagram.Groups.Remove(group);
+ // Act
+ diagram.Groups.Remove(group);
- // Assert
- diagram.Links.Should().BeEmpty();
- }
+ // Assert
+ diagram.Links.Should().BeEmpty();
+ }
- [Fact]
- public void Remove_ShouldRemoveItselfFromParentGroup()
- {
- // Arrange
- var diagram = new TestDiagram();
- var group1 = diagram.Groups.Add(new GroupModel(Array.Empty()));
- var group2 = diagram.Groups.Add(new GroupModel(new[] { group1 }));
+ [Fact]
+ public void Remove_ShouldRemoveItselfFromParentGroup()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var group1 = diagram.Groups.Add(new GroupModel(Array.Empty()));
+ var group2 = diagram.Groups.Add(new GroupModel(new[] { group1 }));
- // Act
- diagram.Groups.Remove(group1);
+ // Act
+ diagram.Groups.Remove(group1);
- // Assert
- group2.Children.Should().BeEmpty();
- group1.Group.Should().BeNull();
- }
+ // Assert
+ group2.Children.Should().BeEmpty();
+ group1.Group.Should().BeNull();
+ }
- [Fact]
- public void Remove_ShouldUngroup()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node = diagram.Nodes.Add(new NodeModel());
- var group = diagram.Groups.Add(new GroupModel(new[] { node }));
+ [Fact]
+ public void Remove_ShouldUngroup()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node = diagram.Nodes.Add(new NodeModel());
+ var group = diagram.Groups.Add(new GroupModel(new[] { node }));
- // Act
- diagram.Groups.Remove(group);
+ // Act
+ diagram.Groups.Remove(group);
- // Assert
- group.Children.Should().BeEmpty();
- node.Group.Should().BeNull();
- }
+ // Assert
+ group.Children.Should().BeEmpty();
+ node.Group.Should().BeNull();
+ }
- [Fact]
- public void Delete_ShouldDeleteChildGroup()
- {
- // Arrange
- var diagram = new TestDiagram();
- var group1 = diagram.Groups.Add(new GroupModel(Array.Empty()));
- var group2 = diagram.Groups.Add(new GroupModel(new[] { group1 }));
+ [Fact]
+ public void Delete_ShouldDeleteChildGroup()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var group1 = diagram.Groups.Add(new GroupModel(Array.Empty()));
+ var group2 = diagram.Groups.Add(new GroupModel(new[] { group1 }));
- // Act
- diagram.Groups.Delete(group2);
+ // Act
+ diagram.Groups.Delete(group2);
- // Assert
- diagram.Groups.Should().BeEmpty();
- }
+ // Assert
+ diagram.Groups.Should().BeEmpty();
+ }
- [Fact]
- public void Delete_ShouldRemoveChild()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node = diagram.Nodes.Add(new NodeModel());
- var group = diagram.Groups.Add(new GroupModel(new[] { node }));
+ [Fact]
+ public void Delete_ShouldRemoveChild()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node = diagram.Nodes.Add(new NodeModel());
+ var group = diagram.Groups.Add(new GroupModel(new[] { node }));
- // Act
- diagram.Groups.Delete(group);
+ // Act
+ diagram.Groups.Delete(group);
- // Assert
- diagram.Groups.Should().BeEmpty();
- diagram.Nodes.Should().BeEmpty();
- }
+ // Assert
+ diagram.Groups.Should().BeEmpty();
+ diagram.Nodes.Should().BeEmpty();
+ }
- [Fact]
- public void Add_ShouldRefreshDiagramOnce()
- {
- // Arrange
- var diagram = new TestDiagram();
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
+ [Fact]
+ public void Add_ShouldRefreshDiagramOnce()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
- // Act
- var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
+ // Act
+ var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
- // Assert
- refreshes.Should().Be(1);
- }
+ // Assert
+ refreshes.Should().Be(1);
+ }
- [Fact]
- public void Remove_ShouldRefreshDiagramOnce()
- {
- // Arrange
- var diagram = new TestDiagram();
- var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
+ [Fact]
+ public void Remove_ShouldRefreshDiagramOnce()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var group = diagram.Groups.Add(new GroupModel(Array.Empty()));
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
- // Act
- diagram.Groups.Remove(group);
+ // Act
+ diagram.Groups.Remove(group);
- // Assert
- refreshes.Should().Be(1);
- }
+ // Assert
+ refreshes.Should().Be(1);
+ }
- [Fact]
- public void Delete_ShouldRefreshDiagramOnce()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node = diagram.Nodes.Add(new NodeModel());
- var group = diagram.Groups.Add(new GroupModel(new[] { node }));
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
-
- // Act
- diagram.Groups.Delete(group);
-
- // Assert
- refreshes.Should().Be(1);
- }
+ [Fact]
+ public void Delete_ShouldRefreshDiagramOnce()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node = diagram.Nodes.Add(new NodeModel());
+ var group = diagram.Groups.Add(new GroupModel(new[] { node }));
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
+
+ // Act
+ diagram.Groups.Delete(group);
+
+ // Assert
+ refreshes.Should().Be(1);
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Layers/NodeLayerTests.cs b/tests/Blazor.Diagrams.Core.Tests/Layers/NodeLayerTests.cs
index 9e2331b0b..7c5718ef4 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Layers/NodeLayerTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Layers/NodeLayerTests.cs
@@ -2,106 +2,105 @@
using FluentAssertions;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Layers
+namespace Blazor.Diagrams.Core.Tests.Layers;
+
+public class NodeLayerTests
{
- public class NodeLayerTests
+ [Fact]
+ public void Remove_ShouldRemoveAllPortLinks()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel());
+ var nodePort1 = node1.AddPort(PortAlignment.Top);
+ var node2 = diagram.Nodes.Add(new NodeModel());
+ var nodePort2 = node2.AddPort(PortAlignment.Top);
+ diagram.Links.Add(new LinkModel(nodePort1, nodePort2));
+
+ // Act
+ diagram.Nodes.Remove(node1);
+
+ // Assert
+ diagram.Links.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Remove_ShouldRemoveAllLinks()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel());
+ var node2 = diagram.Nodes.Add(new NodeModel());
+ diagram.Links.Add(new LinkModel(node1, node2));
+
+ // Act
+ diagram.Nodes.Remove(node1);
+
+ // Assert
+ diagram.Links.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Remove_ShouldRemoveItselfFromParentGroup()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node = diagram.Nodes.Add(new NodeModel());
+ var group = diagram.Groups.Add(new GroupModel(new[] { node }));
+
+ // Act
+ diagram.Nodes.Remove(node);
+
+ // Assert
+ group.Children.Should().BeEmpty();
+ node.Group.Should().BeNull();
+ }
+
+ [Fact]
+ public void Add_ShouldRefreshDiagramOnce()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
+
+ // Act
+ var node = diagram.Nodes.Add(new NodeModel());
+
+ // Assert
+ refreshes.Should().Be(1);
+ }
+
+ [Fact]
+ public void Remove_ShouldRefreshDiagramOnce()
+ {
+ // Arrange
+ var diagram = new TestDiagram();
+ var node1 = diagram.Nodes.Add(new NodeModel());
+ var node2 = diagram.Nodes.Add(new NodeModel());
+ diagram.Links.Add(new LinkModel(node1, node2));
+ var refreshes = 0;
+ diagram.Changed += () => refreshes++;
+
+ // Act
+ diagram.Nodes.Remove(node1);
+
+ // Assert
+ refreshes.Should().Be(1);
+ }
+
+ [Fact]
+ public void Remove_ShouldRemoveControls()
{
- [Fact]
- public void Remove_ShouldRemoveAllPortLinks()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel());
- var nodePort1 = node1.AddPort(PortAlignment.Top);
- var node2 = diagram.Nodes.Add(new NodeModel());
- var nodePort2 = node2.AddPort(PortAlignment.Top);
- diagram.Links.Add(new LinkModel(nodePort1, nodePort2));
-
- // Act
- diagram.Nodes.Remove(node1);
-
- // Assert
- diagram.Links.Should().BeEmpty();
- }
-
- [Fact]
- public void Remove_ShouldRemoveAllLinks()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel());
- var node2 = diagram.Nodes.Add(new NodeModel());
- diagram.Links.Add(new LinkModel(node1, node2));
-
- // Act
- diagram.Nodes.Remove(node1);
-
- // Assert
- diagram.Links.Should().BeEmpty();
- }
-
- [Fact]
- public void Remove_ShouldRemoveItselfFromParentGroup()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node = diagram.Nodes.Add(new NodeModel());
- var group = diagram.Groups.Add(new GroupModel(new[] { node }));
-
- // Act
- diagram.Nodes.Remove(node);
-
- // Assert
- group.Children.Should().BeEmpty();
- node.Group.Should().BeNull();
- }
-
- [Fact]
- public void Add_ShouldRefreshDiagramOnce()
- {
- // Arrange
- var diagram = new TestDiagram();
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
-
- // Act
- var node = diagram.Nodes.Add(new NodeModel());
-
- // Assert
- refreshes.Should().Be(1);
- }
-
- [Fact]
- public void Remove_ShouldRefreshDiagramOnce()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node1 = diagram.Nodes.Add(new NodeModel());
- var node2 = diagram.Nodes.Add(new NodeModel());
- diagram.Links.Add(new LinkModel(node1, node2));
- var refreshes = 0;
- diagram.Changed += () => refreshes++;
-
- // Act
- diagram.Nodes.Remove(node1);
-
- // Assert
- refreshes.Should().Be(1);
- }
-
- [Fact]
- public void Remove_ShouldRemoveControls()
- {
- // Arrange
- var diagram = new TestDiagram();
- var node = diagram.Nodes.Add(new NodeModel());
- var controls = diagram.Controls.AddFor(node);
-
- // Act
- diagram.Nodes.Remove(node);
-
- // Assert
- diagram.Controls.GetFor(node).Should().BeNull();
- }
+ // Arrange
+ var diagram = new TestDiagram();
+ var node = diagram.Nodes.Add(new NodeModel());
+ var controls = diagram.Controls.AddFor(node);
+
+ // Act
+ diagram.Nodes.Remove(node);
+
+ // Assert
+ diagram.Controls.GetFor(node).Should().BeNull();
}
}
diff --git a/tests/Blazor.Diagrams.Core.Tests/Models/Base/BaseLinkModelTests.cs b/tests/Blazor.Diagrams.Core.Tests/Models/Base/BaseLinkModelTests.cs
index 577843a1d..c4566e861 100644
--- a/tests/Blazor.Diagrams.Core.Tests/Models/Base/BaseLinkModelTests.cs
+++ b/tests/Blazor.Diagrams.Core.Tests/Models/Base/BaseLinkModelTests.cs
@@ -7,94 +7,93 @@
using FluentAssertions;
using Xunit;
-namespace Blazor.Diagrams.Core.Tests.Models.Base
+namespace Blazor.Diagrams.Core.Tests.Models.Base;
+
+public class BaseLinkModelTests
{
- public class BaseLinkModelTests
+ [Fact]
+ public void SetSource_ShouldChangePropertiesAndTriggerEvent()
{
- [Fact]
- public void SetSource_ShouldChangePropertiesAndTriggerEvent()
+ // Arrange
+ var link = new LinkModel(sourcePort: new PortModel(null), targetPort: null);
+ var parent = new NodeModel();
+ var port = new PortModel(parent);
+ var sp = new SinglePortAnchor(port);
+ var eventsTriggered = 0;
+ Anchor? oldSp = null;
+ Anchor? newSp = null;
+ BaseLinkModel? linkInstance = null;
+
+ // Act
+ link.SourceChanged += (l, o, n) =>
{
- // Arrange
- var link = new LinkModel(sourcePort: new PortModel(null), targetPort: null);
- var parent = new NodeModel();
- var port = new PortModel(parent);
- var sp = new SinglePortAnchor(port);
- var eventsTriggered = 0;
- Anchor? oldSp = null;
- Anchor? newSp = null;
- BaseLinkModel? linkInstance = null;
+ eventsTriggered++;
+ linkInstance = l;
+ oldSp = o;
+ newSp = n;
+ };
- // Act
- link.SourceChanged += (l, o, n) =>
- {
- eventsTriggered++;
- linkInstance = l;
- oldSp = o;
- newSp = n;
- };
+ link.SetSource(sp);
- link.SetSource(sp);
+ // Assert
+ eventsTriggered.Should().Be(1);
+ link.Source.Should().BeSameAs(sp);
+ oldSp.Should().NotBeNull();
+ newSp.Should().BeSameAs(sp);
+ linkInstance.Should().BeSameAs(link);
+ link.Source.Model.Should().BeSameAs(port);
+ }
- // Assert
- eventsTriggered.Should().Be(1);
- link.Source.Should().BeSameAs(sp);
- oldSp.Should().NotBeNull();
- newSp.Should().BeSameAs(sp);
- linkInstance.Should().BeSameAs(link);
- link.Source.Model.Should().BeSameAs(port);
- }
+ [Fact]
+ public void SetTarget_ShouldChangePropertiesAndTriggerEvent()
+ {
+ // Arrange
+ var link = new LinkModel(new SinglePortAnchor(null), new PositionAnchor(Point.Zero));
+ var parent = new NodeModel();
+ var port = new PortModel(parent);
+ var tp = new SinglePortAnchor(port);
+ var eventsTriggered = 0;
+ Anchor? oldTp = null;
+ Anchor? newTp = null;
+ BaseLinkModel? linkInstance = null;
- [Fact]
- public void SetTarget_ShouldChangePropertiesAndTriggerEvent()
+ // Act
+ link.TargetChanged += (l, o, n) =>
{
- // Arrange
- var link = new LinkModel(new SinglePortAnchor(null), new PositionAnchor(Point.Zero));
- var parent = new NodeModel();
- var port = new PortModel(parent);
- var tp = new SinglePortAnchor(port);
- var eventsTriggered = 0;
- Anchor? oldTp = null;
- Anchor? newTp = null;
- BaseLinkModel? linkInstance = null;
+ eventsTriggered++;
+ linkInstance = l;
+ oldTp = o;
+ newTp = n;
+ };
- // Act
- link.TargetChanged += (l, o, n) =>
- {
- eventsTriggered++;
- linkInstance = l;
- oldTp = o;
- newTp = n;
- };
+ link.SetTarget(tp);
- link.SetTarget(tp);
-
- // Assert
- eventsTriggered.Should().Be(1);
- link.Target.Should().BeSameAs(tp);
- oldTp.Should().BeOfType();
- newTp.Should().BeSameAs(tp);
- linkInstance.Should().BeSameAs(link);
- link.Target!.Model.Should().BeSameAs(port);
- }
+ // Assert
+ eventsTriggered.Should().Be(1);
+ link.Target.Should().BeSameAs(tp);
+ oldTp.Should().BeOfType();
+ newTp.Should().BeSameAs(tp);
+ linkInstance.Should().BeSameAs(link);
+ link.Target!.Model.Should().BeSameAs(port);
+ }
- [Fact]
- public void GetBounds_ShouldReturnPathBBox()
- {
- // Arrange
- var link = new LinkModel(new PositionAnchor(new Point(10, 5)), new PositionAnchor(new Point(100, 80)));
- link.Diagram = new TestDiagram();
- link.PathGenerator = new StraightPathGenerator();
- link.Router = new NormalRouter();
+ [Fact]
+ public void GetBounds_ShouldReturnPathBBox()
+ {
+ // Arrange
+ var link = new LinkModel(new PositionAnchor(new Point(10, 5)), new PositionAnchor(new Point(100, 80)));
+ link.Diagram = new TestDiagram();
+ link.PathGenerator = new StraightPathGenerator();
+ link.Router = new NormalRouter();
- // Act
- link.Refresh();
- var bounds = link.GetBounds()!;
+ // Act
+ link.Refresh();
+ var bounds = link.GetBounds()!;
- // Assert
- bounds.Left.Should().Be(10);
- bounds.Top.Should().Be(5);
- bounds.Width.Should().Be(90);
- bounds.Height.Should().Be(75);
- }
+ // Assert
+ bounds.Left.Should().Be(10);
+ bounds.Top.Should().Be(5);
+ bounds.Width.Should().Be(90);
+ bounds.Height.Should().Be(75);
}
}
diff --git a/tests/Blazor.Diagrams.Tests/Components/LinkVertexWidgetTests.cs b/tests/Blazor.Diagrams.Tests/Components/LinkVertexWidgetTests.cs
index fb8128020..9c4678099 100644
--- a/tests/Blazor.Diagrams.Tests/Components/LinkVertexWidgetTests.cs
+++ b/tests/Blazor.Diagrams.Tests/Components/LinkVertexWidgetTests.cs
@@ -8,128 +8,127 @@
using System.Threading.Tasks;
using Xunit;
-namespace Blazor.Diagrams.Tests.Components
+namespace Blazor.Diagrams.Tests.Components;
+
+public class LinkVertexWidgetTests
{
- public class LinkVertexWidgetTests
+ [Fact]
+ public void ShouldRenderCircle()
+ {
+ // Arrange
+ using var ctx = new TestContext();
+ var node1 = new NodeModel();
+ var node2 = new NodeModel();
+ var link = new LinkModel(node1, node2);
+ var vertex = new LinkVertexModel(link, new Point(10.5, 20));
+ link.Vertices.Add(vertex);
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.Vertex, vertex)
+ .Add(n => n.Color, "red")
+ .Add(n => n.SelectedColor, "blue")
+ .Add(n => n.BlazorDiagram, new BlazorDiagram()));
+
+ // Assert
+ cut.MarkupMatches(" ");
+ }
+
+ [Fact]
+ public void ShouldRenderCircleWithSelectedColor_WhenVertexIsSelected()
+ {
+ // Arrange
+ using var ctx = new TestContext();
+ var node1 = new NodeModel();
+ var node2 = new NodeModel();
+ var link = new LinkModel(node1, node2);
+ var vertex = new LinkVertexModel(link, new Point(10.5, 20));
+ link.Vertices.Add(vertex);
+ vertex.Selected = true;
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.Vertex, vertex)
+ .Add(n => n.Color, "red")
+ .Add(n => n.SelectedColor, "blue")
+ .Add(n => n.BlazorDiagram, new BlazorDiagram()));
+
+ // Assert
+ cut.MarkupMatches(" ");
+ }
+
+ [Fact]
+ public void ShouldRerender_WhenVertexIsRefreshed()
+ {
+ // Arrange
+ using var ctx = new TestContext();
+ var node1 = new NodeModel();
+ var node2 = new NodeModel();
+ var link = new LinkModel(node1, node2);
+ var vertex = new LinkVertexModel(link, new Point(10.5, 20));
+ link.Vertices.Add(vertex);
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.Vertex, vertex)
+ .Add(n => n.Color, "red")
+ .Add(n => n.SelectedColor, "blue")
+ .Add(n => n.BlazorDiagram, new BlazorDiagram()));
+
+ // Assert
+ cut.RenderCount.Should().Be(1);
+ vertex.Refresh();
+ cut.RenderCount.Should().Be(2);
+ }
+
+ [Fact]
+ public async Task ShouldDeleteItselfAndRefreshParent_WhenDoubleClicked()
+ {
+ // Arrange
+ using var ctx = new TestContext();
+ var node1 = new NodeModel();
+ var node2 = new NodeModel();
+ var link = new LinkModel(node1, node2);
+ int linkRefreshes = 0;
+ var vertex = new LinkVertexModel(link, new Point(10.5, 20));
+ link.Vertices.Add(vertex);
+ link.Changed += _ => linkRefreshes++;
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.Vertex, vertex)
+ .Add(n => n.Color, "red")
+ .Add(n => n.SelectedColor, "blue")
+ .Add(n => n.BlazorDiagram, new BlazorDiagram()));
+
+ await cut.Find("circle").DoubleClickAsync(new MouseEventArgs());
+
+ // Assert
+ link.Vertices.Should().BeEmpty();
+ linkRefreshes.Should().Be(1);
+ }
+
+ [Fact]
+ public void ShouldUseCustomComponent_WhenProvided()
{
- [Fact]
- public void ShouldRenderCircle()
- {
- // Arrange
- using var ctx = new TestContext();
- var node1 = new NodeModel();
- var node2 = new NodeModel();
- var link = new LinkModel(node1, node2);
- var vertex = new LinkVertexModel(link, new Point(10.5, 20));
- link.Vertices.Add(vertex);
-
- // Act
- var cut = ctx.RenderComponent(parameters => parameters
- .Add(n => n.Vertex, vertex)
- .Add(n => n.Color, "red")
- .Add(n => n.SelectedColor, "blue")
- .Add(n => n.BlazorDiagram, new BlazorDiagram()));
-
- // Assert
- cut.MarkupMatches(" ");
- }
-
- [Fact]
- public void ShouldRenderCircleWithSelectedColor_WhenVertexIsSelected()
- {
- // Arrange
- using var ctx = new TestContext();
- var node1 = new NodeModel();
- var node2 = new NodeModel();
- var link = new LinkModel(node1, node2);
- var vertex = new LinkVertexModel(link, new Point(10.5, 20));
- link.Vertices.Add(vertex);
- vertex.Selected = true;
-
- // Act
- var cut = ctx.RenderComponent(parameters => parameters
- .Add(n => n.Vertex, vertex)
- .Add(n => n.Color, "red")
- .Add(n => n.SelectedColor, "blue")
- .Add(n => n.BlazorDiagram, new BlazorDiagram()));
-
- // Assert
- cut.MarkupMatches(" ");
- }
-
- [Fact]
- public void ShouldRerender_WhenVertexIsRefreshed()
- {
- // Arrange
- using var ctx = new TestContext();
- var node1 = new NodeModel();
- var node2 = new NodeModel();
- var link = new LinkModel(node1, node2);
- var vertex = new LinkVertexModel(link, new Point(10.5, 20));
- link.Vertices.Add(vertex);
-
- // Act
- var cut = ctx.RenderComponent(parameters => parameters
- .Add(n => n.Vertex, vertex)
- .Add(n => n.Color, "red")
- .Add(n => n.SelectedColor, "blue")
- .Add(n => n.BlazorDiagram, new BlazorDiagram()));
-
- // Assert
- cut.RenderCount.Should().Be(1);
- vertex.Refresh();
- cut.RenderCount.Should().Be(2);
- }
-
- [Fact]
- public async Task ShouldDeleteItselfAndRefreshParent_WhenDoubleClicked()
- {
- // Arrange
- using var ctx = new TestContext();
- var node1 = new NodeModel();
- var node2 = new NodeModel();
- var link = new LinkModel(node1, node2);
- int linkRefreshes = 0;
- var vertex = new LinkVertexModel(link, new Point(10.5, 20));
- link.Vertices.Add(vertex);
- link.Changed += _ => linkRefreshes++;
-
- // Act
- var cut = ctx.RenderComponent(parameters => parameters
- .Add(n => n.Vertex, vertex)
- .Add(n => n.Color, "red")
- .Add(n => n.SelectedColor, "blue")
- .Add(n => n.BlazorDiagram, new BlazorDiagram()));
-
- await cut.Find("circle").DoubleClickAsync(new MouseEventArgs());
-
- // Assert
- link.Vertices.Should().BeEmpty();
- linkRefreshes.Should().Be(1);
- }
-
- [Fact]
- public void ShouldUseCustomComponent_WhenProvided()
- {
- // Arrange
- using var ctx = new TestContext();
- var diagram = new BlazorDiagram();
- diagram.RegisterComponent();
- var node1 = new NodeModel();
- var node2 = new NodeModel();
- var link = new LinkModel(node1, node2);
- var vertex = new LinkVertexModel(link, new Point(10.5, 20));
- link.Vertices.Add(vertex);
-
- // Act
- var cut = ctx.RenderComponent(parameters => parameters
- .Add(n => n.Vertex, vertex)
- .Add(n => n.Color, "red")
- .Add(n => n.SelectedColor, "blue")
- .Add(n => n.BlazorDiagram, diagram));
-
- // Assert
- cut.MarkupMatches(" ");
- }
+ // Arrange
+ using var ctx = new TestContext();
+ var diagram = new BlazorDiagram();
+ diagram.RegisterComponent();
+ var node1 = new NodeModel();
+ var node2 = new NodeModel();
+ var link = new LinkModel(node1, node2);
+ var vertex = new LinkVertexModel(link, new Point(10.5, 20));
+ link.Vertices.Add(vertex);
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.Vertex, vertex)
+ .Add(n => n.Color, "red")
+ .Add(n => n.SelectedColor, "blue")
+ .Add(n => n.BlazorDiagram, diagram));
+
+ // Assert
+ cut.MarkupMatches(" ");
}
}
diff --git a/tests/Blazor.Diagrams.Tests/Components/NodeWidgetTests.cs b/tests/Blazor.Diagrams.Tests/Components/NodeWidgetTests.cs
index 9e8ba3239..dfabb012d 100644
--- a/tests/Blazor.Diagrams.Tests/Components/NodeWidgetTests.cs
+++ b/tests/Blazor.Diagrams.Tests/Components/NodeWidgetTests.cs
@@ -9,29 +9,28 @@
using Xunit;
-namespace Blazor.Diagrams.Tests.Components
+namespace Blazor.Diagrams.Tests.Components;
+
+public class NodeWidgetTests
{
- public class NodeWidgetTests
+ [Fact]
+ public void DefaultNodeWidget_ShouldHaveSingleClassAndNoPorts_WhenItHasNoPortsAndNoSelectionNorGroup()
{
- [Fact]
- public void DefaultNodeWidget_ShouldHaveSingleClassAndNoPorts_WhenItHasNoPortsAndNoSelectionNorGroup()
- {
- // Arrange
- using var ctx = new TestContext();
- var node = new NodeModel(Point.Zero);
-
- // Act
- var cut = ctx.RenderComponent(parameters => parameters
- .Add(n => n.Node, node));
-
- // Assert
- var content = cut.Find("div.default-node");
- content.ClassList.Should().ContainSingle();
- content.ClassList[0].Should().Be("default-node");
- content.TextContent.Trim().Should().Be("Title");
-
- var ports = cut.FindComponents();
- ports.Should().BeEmpty();
- }
+ // Arrange
+ using var ctx = new TestContext();
+ var node = new NodeModel(Point.Zero);
+
+ // Act
+ var cut = ctx.RenderComponent(parameters => parameters
+ .Add(n => n.Node, node));
+
+ // Assert
+ var content = cut.Find("div.default-node");
+ content.ClassList.Should().ContainSingle();
+ content.ClassList[0].Should().Be("default-node");
+ content.TextContent.Trim().Should().Be("Title");
+
+ var ports = cut.FindComponents();
+ ports.Should().BeEmpty();
}
}
diff --git a/tests/Blazor.Diagrams.Tests/Components/SvgNodeWidgetTests.cs b/tests/Blazor.Diagrams.Tests/Components/SvgNodeWidgetTests.cs
index e13500b4a..64634c5dd 100644
--- a/tests/Blazor.Diagrams.Tests/Components/SvgNodeWidgetTests.cs
+++ b/tests/Blazor.Diagrams.Tests/Components/SvgNodeWidgetTests.cs
@@ -3,21 +3,20 @@
using Bunit;
using Xunit;
-namespace Blazor.Diagrams.Tests.Components
+namespace Blazor.Diagrams.Tests.Components;
+
+public class SvgNodeWidgetTests
{
- public class SvgNodeWidgetTests
+ [Fact]
+ public void ShouldRenderSimpleRect()
{
- [Fact]
- public void ShouldRenderSimpleRect()
- {
- // Arrange
- using var ctx = new TestContext();
+ // Arrange
+ using var ctx = new TestContext();
- // Act
- var cut = ctx.RenderComponent();
+ // Act
+ var cut = ctx.RenderComponent();
- // Assert
- cut.MarkupMatches(" ");
- }
+ // Assert
+ cut.MarkupMatches(" ");
}
}
diff --git a/tests/Blazor.Diagrams.Tests/DiagramTests.cs b/tests/Blazor.Diagrams.Tests/DiagramTests.cs
index 90a93fa91..a61abc64b 100644
--- a/tests/Blazor.Diagrams.Tests/DiagramTests.cs
+++ b/tests/Blazor.Diagrams.Tests/DiagramTests.cs
@@ -5,81 +5,80 @@
using Microsoft.AspNetCore.Components;
using Xunit;
-namespace Blazor.Diagrams.Tests
+namespace Blazor.Diagrams.Tests;
+
+public class DiagramTests
{
- public class DiagramTests
+ [Fact]
+ public void GetComponentForModel_ShouldReturnComponentType_WhenModelTypeWasRegistered()
+ {
+ // Arrange
+ var diagram = new BlazorDiagram();
+ diagram.RegisterComponent();
+
+ // Act
+ var componentType = diagram.GetComponent();
+
+ // Assert
+ componentType.Should().Be(typeof(NodeWidget));
+ }
+
+ [Fact]
+ public void GetComponentForModel_ShouldReturnNull_WhenModelTypeWasNotRegistered()
+ {
+ // Arrange
+ var diagram = new BlazorDiagram();
+
+ // Act
+ var componentType = diagram.GetComponent();
+
+ // Assert
+ componentType.Should().BeNull();
+ }
+
+ [Fact]
+ public void GetComponentForModel_ShouldReturnComponentType_WhenInheritedModelTypeWasRegistered()
+ {
+ // Arrange
+ var diagram = new BlazorDiagram();
+ diagram.RegisterComponent();
+
+ // Act
+ var componentType = diagram.GetComponent();
+
+ // Assert
+ componentType.Should().Be(typeof(NodeWidget));
+ }
+
+ [Fact]
+ public void GetComponentForModel_ShouldReturnSpecificComponentType_WhenInheritedAndSpecificModelTypeWasRegistered()
+ {
+ // Arrange
+ var diagram = new BlazorDiagram();
+ diagram.RegisterComponent();
+ diagram.RegisterComponent();
+
+ // Act
+ var componentType = diagram.GetComponent();
+
+ // Assert
+ componentType.Should().Be(typeof(CustomWidget));
+ }
+
+ [Fact]
+ public void GetComponentForModel_ShouldReturnNull_WhenCheckSubclassesIsFalse()
{
- [Fact]
- public void GetComponentForModel_ShouldReturnComponentType_WhenModelTypeWasRegistered()
- {
- // Arrange
- var diagram = new BlazorDiagram();
- diagram.RegisterComponent