Skip to content

Commit

Permalink
Merge pull request #101 from neuroglia-io/fix-dagre-refactor
Browse files Browse the repository at this point in the history
Replaced Guid by string for ids in Neuroglia.Blazor.Dagre
  • Loading branch information
cdavernas authored Jul 25, 2024
2 parents 8858c53 + 852deed commit 9f6d9e2
Show file tree
Hide file tree
Showing 39 changed files with 685 additions and 378 deletions.
12 changes: 6 additions & 6 deletions src/Neuroglia.Blazor.Dagre/DagreGraph.razor
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
<CascadingValue Value="graph">
<svg @ref="graphReference"
class="graph-canvas @graph.CssClass"
width="@(graph.Width?.ToString() ?? "100%")"
height="@(graph.Height?.ToString() ?? "100%")"
width="@(graph.Width != 0 ? graph.Width.ToString() : "100%")"
height="@(graph.Height != 0 ? graph.Height.ToString() : "100%")"
@onmouseenter="HandleOnMouseEnter"
@onmouseleave="HandleOnMouseLeave"
@onmousedown="HandleOnMouseDown"
Expand All @@ -39,7 +39,7 @@
@onwheel="HandleOnWheel"
>
<defs>
@foreach(var def in graph.SvgDefinitionComponents)
@foreach(var def in graph.ReferenceableComponentTypes)
{
<DynamicComponent @key="def" Type="def" />
}
Expand All @@ -53,7 +53,7 @@
{
@if (cluster != null)
{
<InteractiveDynamicComponent @key="cluster" Type="graph.GetComponentTypeAsync(cluster)" Parameters="GetComponentParameter(cluster)" />
<InteractiveDynamicComponent @key="cluster" Type="graph.GetComponentType(cluster)" Parameters="GetComponentParameter(cluster)" />
}
}
</g>
Expand All @@ -62,7 +62,7 @@
{
@if (edge != null)
{
<DynamicComponent @key="edge" Type="graph.GetComponentTypeAsync(edge)" Parameters="GetComponentParameter(edge)" />
<DynamicComponent @key="edge" Type="graph.GetComponentType(edge)" Parameters="GetComponentParameter(edge)" />
}
}
</g>
Expand All @@ -71,7 +71,7 @@
{
@if (node != null)
{
<InteractiveDynamicComponent @key="node" Type="graph.GetComponentTypeAsync(node)" Parameters="GetComponentParameter(node)" />
<InteractiveDynamicComponent @key="node" Type="graph.GetComponentType(node)" Parameters="GetComponentParameter(node)" />
}
}
</g>
Expand Down
18 changes: 9 additions & 9 deletions src/Neuroglia.Blazor.Dagre/DagreService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,29 @@ public virtual async Task<IGraphViewModel> ComputePositionsAsync(IGraphViewModel
}
// build the dagre/graphlib graph
var graph = await this.GraphAsync(options);
var nodes = graphViewModel.AllNodes.Values.Concat( graphViewModel.AllClusters.Values);
var nodes = graphViewModel.AllNodes.Values.Concat(graphViewModel.AllClusters.Values);
foreach (var node in nodes)
{
await graph.SetNodeAsync(node.Id.ToString(), node);
if (node.ParentId != null) await graph.SetParentAsync(node.Id.ToString(), node.ParentId.ToString()!);
await graph.SetNodeAsync(node.Id, node);
if (node.ParentId != null) await graph.SetParentAsync(node.Id, node.ParentId!);
}
foreach(var edge in graphViewModel.Edges.Values)
{
if (options?.Multigraph == true) await graph.SetEdgeAsync(edge.SourceId.ToString(), edge.TargetId.ToString(), edge, edge.Id.ToString());
else await graph.SetEdgeAsync(edge.SourceId.ToString(), edge.TargetId.ToString(), edge);
if (options?.Multigraph == true) await graph.SetEdgeAsync(edge.SourceId, edge.TargetId, edge, edge.Id);
else await graph.SetEdgeAsync(edge.SourceId, edge.TargetId, edge);
}
await this.LayoutAsync(graph);
// update our view models with the computed values
foreach (var node in nodes)
{
var graphNode = await graph.NodeAsync(node.Id.ToString());
node.SetGeometry(graphNode.X, graphNode.Y, graphNode.Width, graphNode.Height);
var graphNode = await graph.NodeAsync(node.Id);
node.SetBounds(graphNode.X, graphNode.Y, graphNode.Width, graphNode.Height);
}
foreach (var edge in graphViewModel.Edges.Values)
{
GraphLibEdge graphEdge;
if (options?.Multigraph == true) graphEdge = await graph.EdgeAsync(edge.SourceId.ToString(), edge.TargetId.ToString(), edge.Id.ToString());
else graphEdge = await graph.EdgeAsync(edge.SourceId.ToString(), edge.TargetId.ToString());
if (options?.Multigraph == true) graphEdge = await graph.EdgeAsync(edge.SourceId, edge.TargetId, edge.Id);
else graphEdge = await graph.EdgeAsync(edge.SourceId, edge.TargetId);
if (graphEdge?.Points != null) edge.Points = [.. graphEdge.Points];
}
graphViewModel.DagreGraph = graph;
Expand Down
47 changes: 23 additions & 24 deletions src/Neuroglia.Blazor.Dagre/Models/BoundingBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,30 @@

namespace Neuroglia.Blazor.Dagre.Models;

public class BoundingBox
: IBoundingBox
/// <summary>
/// Represents the bounding box of a 2D shape
/// </summary>
/// <param name="Position">The position</param>
/// <param name="Size">The size</param>
/// <remarks>
/// Initializes a <see cref="BoundingBox"/> using the given parameters
/// </remarks>
/// <param name="width">The width of the <see cref="BoundingBox"/></param>
/// <param name="height">The height of the <see cref="BoundingBox"/></param>
/// <param name="x">The horizontal position of the <see cref="BoundingBox"/></param>
/// <param name="y">The vertical position of the <see cref="BoundingBox"/></param>
public class BoundingBox(double width = 0, double height = 0, double x = 0, double y = 0)
: IPositionable, ISizeable
{
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? X { get; set; }
/// <inheritdoc/>
public double X { get; set; } = x;

[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? Y { get; set; }
/// <inheritdoc/>
public double Y { get; set; } = y;

[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? Width { get; set; }
/// <inheritdoc/>
public double Width { get; set; } = width;

[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? Height { get; set; }

public BoundingBox() { }

public BoundingBox(double? width, double? height, double? x, double? y) {
this.Width = width;
this.Height = height;
this.X = x;
this.Y = y;
}
}
/// <inheritdoc/>
public double Height { get; set; } = height;
}
73 changes: 52 additions & 21 deletions src/Neuroglia.Blazor.Dagre/Models/ClusterViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,55 @@

namespace Neuroglia.Blazor.Dagre.Models;

/// <summary>
/// Represents the default implementation of the <see cref="IClusterViewModel"/>
/// </summary>
public class ClusterViewModel
: NodeViewModel, IClusterViewModel
{
protected readonly Dictionary<Guid, INodeViewModel> _children;
public virtual IReadOnlyDictionary<Guid, INodeViewModel> Children => this._children;
protected readonly Dictionary<string, INodeViewModel> _children;
/// <inheritdoc/>
public virtual IReadOnlyDictionary<string, INodeViewModel> Children => this._children;

protected readonly Dictionary<Guid, INodeViewModel> _allNodes;
public virtual IReadOnlyDictionary<Guid, INodeViewModel> AllNodes => this._allNodes;
protected readonly Dictionary<string, INodeViewModel> _allNodes;
/// <inheritdoc/>
public virtual IReadOnlyDictionary<string, INodeViewModel> AllNodes => this._allNodes;

protected readonly Dictionary<Guid, IClusterViewModel> _allClusters;
public virtual IReadOnlyDictionary<Guid, IClusterViewModel> AllClusters => this._allClusters;
protected readonly Dictionary<string, IClusterViewModel> _allClusters;
/// <inheritdoc/>
public virtual IReadOnlyDictionary<string, IClusterViewModel> AllClusters => this._allClusters;

/// <inheritdoc/>
public event Action<INodeViewModel>? ChildAdded;

/// <summary>
/// Initializes a new <see cref="ClusterViewModel"/>
/// </summary>
/// <param name="children"></param>
/// <param name="label"></param>
/// <param name="cssClass"></param>
/// <param name="shape"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="radiusX"></param>
/// <param name="radiusY"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="componentType"></param>
/// <param name="parentId"></param>
public ClusterViewModel(
Dictionary<Guid, INodeViewModel>? children = null,
Dictionary<string, INodeViewModel>? children = null,
string? label = "",
string? cssClass = null,
string? shape = null,
double? width = Constants.ClusterWidth,
double? height = Constants.ClusterHeight,
double? radiusX = Constants.ClusterRadius,
double? radiusY = Constants.ClusterRadius,
double? x = 0,
double? y = 0,
double width = Constants.ClusterWidth,
double height = Constants.ClusterHeight,
double radiusX = Constants.ClusterRadius,
double radiusY = Constants.ClusterRadius,
double x = 0,
double y = 0,
Type? componentType = null,
Guid? parentId = null
string? parentId = null
)
: base(label, cssClass, shape, width, height, radiusX, radiusY, x, y, componentType, parentId)
{
Expand All @@ -66,10 +88,17 @@ public ClusterViewModel(
}
}

/// <summary>
/// Moves the <see cref="ClusterViewModel"/>
/// </summary>
/// <param name="deltaX">The horizontal distance</param>
/// <param name="deltaY">The veritcal distance</param>
public override void Move(double deltaX, double deltaY)
{
if (deltaX == 0 && deltaY == 0)
{
return;
}
base.Move(deltaX, deltaY);
foreach(var child in this._children.Values)
{
Expand All @@ -83,7 +112,7 @@ public override void Move(double deltaX, double deltaY)
/// <param name="node"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public virtual async Task AddChildAsync(INodeViewModel node)
public virtual void AddChild(INodeViewModel node)
{
ArgumentNullException.ThrowIfNull(node);
node.ParentId = this.Id;
Expand All @@ -98,7 +127,6 @@ public virtual async Task AddChildAsync(INodeViewModel node)
}
this._allNodes.Add(node.Id, node);
this.OnChange();
await Task.CompletedTask;
}

/// <summary>
Expand All @@ -125,16 +153,19 @@ protected virtual void Flatten(IClusterViewModel cluster)
}
}

/// <summary>
/// Handles changes to the cluster's children
/// </summary>
protected virtual void OnChildChanged()
{
var minX = this.Children.Values.Select(node => node.X - (node.Width ?? 0) / 2).Min();
var maxX = this.Children.Values.Select(node => node.X + (node.Width ?? 0) / 2).Max();
var minY = this.Children.Values.Select(node => node.Y - (node.Height ?? 0) / 2).Min();
var maxY = this.Children.Values.Select(node => node.Y + (node.Height ?? 0) / 2).Max();
var minX = this.Children.Values.Select(node => node.X - node.Width / 2).Min();
var maxX = this.Children.Values.Select(node => node.X + node.Width / 2).Max();
var minY = this.Children.Values.Select(node => node.Y - node.Height / 2).Min();
var maxY = this.Children.Values.Select(node => node.Y + node.Height / 2).Max();
var x = (minX + maxX) / 2;
var y = (minY + maxY) / 2;
var width = maxX - minX + Constants.ClusterPaddingX;
var height = maxY - minY + Constants.ClusterPaddingY;
this.SetGeometry(x, y, width, height);
this.SetBounds(x, y, width, height);
}
}
12 changes: 4 additions & 8 deletions src/Neuroglia.Blazor.Dagre/Models/DagreGraphConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,10 @@ public class DagreGraphConfig
public virtual string? Ranker { get; set; }

/// <inheritdoc />
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? Width { get; set; }
public virtual double Width { get; set; }

/// <inheritdoc />
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? Height { get; set; }
public virtual double Height { get; set; }

/// <inheritdoc />
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
Expand Down Expand Up @@ -138,8 +134,8 @@ public DagreGraphConfig(
this.MarginY = marginY;
this.Acyclicer = acyclicer;
this.Ranker = ranker;
this.Width = width;
this.Height = height;
this.Width = width ?? 0;
this.Height = height ?? 0;
this.Label = label;
this.Metadata = metadata;
}
Expand Down
30 changes: 20 additions & 10 deletions src/Neuroglia.Blazor.Dagre/Models/EdgeViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Collections.ObjectModel;

namespace Neuroglia.Blazor.Dagre.Models;

public class EdgeViewModel(Guid sourceId, Guid targetId, string? label = null, string? cssClass = null, string? shape = null, ICollection<IPosition>? points = null, Type? componentType = null)
public class EdgeViewModel(string sourceId, string targetId, string? label = null, string? cssClass = null, string? shape = null, IEnumerable<IPositionable>? points = null, Type? componentType = null)
: GraphElement(label, cssClass, componentType), IEdgeViewModel
{
/// <inheritdoc/>
public virtual string SourceId { get; set; } = sourceId;

public virtual Guid SourceId { get; set; } = sourceId;
public virtual Guid TargetId { get; set; } = targetId;
/// <inheritdoc/>
public virtual string TargetId { get; set; } = targetId;

private ICollection<IPosition> _points = points ?? [];
public virtual ICollection<IPosition> Points {
private IEnumerable<IPositionable> _points = points ?? [];
/// <inheritdoc/>
public virtual IEnumerable<IPositionable> Points {
get => this._points;
set
{
this._points = value;
this._points = [..value];
var minX = this.Points.Min(p => p.X);
var maxX = this.Points.Max(p => p.X);
var minY = this.Points.Min(p => p.Y);
Expand All @@ -36,21 +37,30 @@ public virtual ICollection<IPosition> Points {
var height = maxY - minY;
var x = minX + width / 2;
var y = minY + height / 2;
this.BBox = new BoundingBox(width, height, x, y);
this.Bounds = new BoundingBox(width, height, x, y);
}
}

/// <inheritdoc/>
[System.Text.Json.Serialization.JsonPropertyName("labelpos")]
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(PropertyName = "labelpos", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual string? LabelPosition { get; set; }

/// <inheritdoc/>
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual double? LabelOffset { get; set; }

/// <inheritdoc/>
public virtual string Shape { get; set; } = shape ?? EdgeShape.BSpline;

/// <inheritdoc/>
public virtual string? StartMarkerId { get; set; }

/// <inheritdoc/>
public virtual string? EndMarkerId { get; set; } = Constants.EdgeEndArrowId;
public virtual IBoundingBox BBox { get; set; } = new BoundingBox();

/// <inheritdoc/>
public virtual BoundingBox Bounds { get; set; } = new BoundingBox();
}
7 changes: 2 additions & 5 deletions src/Neuroglia.Blazor.Dagre/Models/GraphElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public abstract class GraphElement
: IGraphElement
{
/// <inheritdoc />
public virtual Guid Id { get; set; } = Guid.NewGuid();
public virtual string Id { get; set; } = Guid.NewGuid().ToString();

/// <summary>
/// Stores the element's label
Expand Down Expand Up @@ -119,9 +119,6 @@ protected GraphElement(string? label = "", string? cssClass = null, Type? compon
/// <summary>
/// Invokes the change action
/// </summary>
protected virtual void OnChange()
{
this.Changed?.Invoke();
}
protected virtual void OnChange() => this.Changed?.Invoke();

}
2 changes: 1 addition & 1 deletion src/Neuroglia.Blazor.Dagre/Models/GraphLibEdge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ public class GraphLibEdge
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
[Newtonsoft.Json.JsonProperty(NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public virtual Position[]? Points { get; set; }
IPosition[]? IGraphLibEdge.Points { get => this.Points; set => this.Points = value?.OfType<Position>()?.ToArray(); }
IPositionable[]? IGraphLibEdge.Points { get => this.Points; set => this.Points = value?.OfType<Position>()?.ToArray(); }
}
Loading

0 comments on commit 9f6d9e2

Please sign in to comment.