excubo-ag / Blazor.Diagrams

https://excubo-ag.github.io/Blazor.Diagrams/
MIT License
136 stars 18 forks source link

Diagram LinkModified LinkBase param doesnt provide correct positions on source and node #83

Closed antwjadam closed 3 years ago

antwjadam commented 3 years ago

On my links I have

    <Links OnRemove="LinkRemoved" OnModified="LinkChanged" @bind-DefaultArrow="arrows">

Which has this supporting code

  private void LinkChanged(LinkBase link) {
    LinkNodes(link);
  }

  private async Task LinkNodes(LinkBase link) {
    // this is fired when the second end of a link is clicked into place, completing a link added.
    log.LogDebug(LoggingType.BlazorEvent, "Page {page} : {method} link modified from Node {linkSource} {sourcePort} to Node {linkTarget} {targetPort}.",
      nameof(Index), nameof(LinkNodes), link.Source.Node.Id, link.Source.Port, link.Target.Node.Id, link.Target.Port);

// other code here obfuscated but need the correct ports returned,
  }

No matter which point on the node I draw the link, when placing the link end, the source and target nodes are correct BUT the ports are always returning as "Any" instead of the actual port the link is connected to.

Please can the ports be fixed to be correct? Thanks in advance

stefanloerwald commented 3 years ago

Hi @antwjadam,

Placing a link manually will never result in a non-Any value for Port. Manually drawn links are always placed with coordinates
relative to the node. It is simply not a feature of the library (yet). If you need it, I think it could be added quite easily, by comparing the link relative coordinates to the port coordinates. I'm happy to review a PR on this topic.

Best regards Stefan

antwjadam commented 3 years ago

Yeah, Ive been tasked with creating a diagram view of our server network and then dynamically adjust the status of the servers by changing colours traffic light style, This mean storing the diagram nodes and links onto a database so that I can then link the nodes to the monitoring results. At the moment I can't see an easy way of converting the relative X Y or the X Y of the node/link ends to the correct port so was hoping that drawing a new link on the diagram when clicking the node ports would return the port clicked in the modified params, unfortunately it doesnt. The project is in early stages right now, so no great rush needed for this fix, at the moment my code does this,,,

SourcePort = link.Source.Port == Position.Any ? Position.Bottom : link.Source.Port TargetPort = link.Target.Port == Position.Any ? Position.Top : link.Target.Port

Which means when the user reloads the diagram, the links always get redrawn from bottom port to top port - affecting the look of the diagram when initially drawn. But coding this way means that when the correct port starts getting returned in the LinkBase of the event, the code should then work as I originally intend and not change the ports in use for a link.

I also haven't yet found a way to get the diagram to reload manually after saving the link except re-navigating to the same page forcing a whole page redraw. Ideally, I just want to retrigger the diagram itself so it goes through the Onsetparams, initialize, rendering and after render code. Still learning to use this as it seems the most lightweight choice for blazor and what I am trying to achieve,

Many thanks for your time and attention

stefanloerwald commented 3 years ago

The behavior would need to change in the library, about here: https://github.com/excubo-ag/Blazor.Diagrams/blob/ade9041b51ff7dffba7ac94fea065a393b0b7b48/Diagram/Diagram.MouseInteraction.razor.cs#L444

There, the relative coordinates would need to be compared with all the coordinates return by NodeBase.GetDefaultPort(Position.TopLeft), NodeBase.GetDefaultPort(Position.Top), ... and the nearest would win.

I would propose creating a new property on Links, e.g. "SnapToPort", which allows us to keep the existing behavior as default and an opt-in into what you need, with the implementation as outlined above.

Please let me know if you're up for it to make that PR. Stefan

antwjadam commented 3 years ago

That sounds like a good way forward, Thanks.

antwjadam commented 3 years ago

This code seems to work for me (still some more tests to go).

It goes to the top of the modified event on the link and only affects links that terminate at a node at both ends.

Posting it here so others who might need it can adapt for their purposes. (It needs a bit more refactoring but I didnt want to obfuscate the code clarity too much. Hope this is useful

// support properties for assessing node connection when manually drawing the link private double differenceXY; private Position defaultPort;

// support properties for checking if a node port is the one potentially clicked on private void CheckForNodePort(NodeAnchor anchor, Position port, string portText) { var nodePort = anchor.Node.GetDefaultPort(port); var xDiff = nodePort.RelativeX - anchor.RelativeX; var yDiff = nodePort.RelativeY - anchor.RelativeY; var nodeDiff = (xDiff xDiff) + (yDiff yDiff); log.LogDebug(LoggingType.BlazorEvent, "Node Port {portText} {rX},{rY} - Difference {diff}.", portText, nodePort.RelativeX, nodePort.RelativeY, nodeDiff); if (nodeDiff < differenceXY) { differenceXY = nodeDiff; defaultPort = Position.Left; } }

// support method for determining node pot the link is linked to private void SnapLinkToPort(NodeAnchor anchor) { differenceXY = double.MaxValue; defaultPort = Position.Any;

CheckForNodePort(anchor, Position.Left,        "Left          ");
CheckForNodePort(anchor, Position.TopLeft,     "Top Left      ");
CheckForNodePort(anchor, Position.Top,         "Top           ");
CheckForNodePort(anchor, Position.TopRight,    "Top Right     ");
CheckForNodePort(anchor, Position.Right,       "Right         ");
CheckForNodePort(anchor, Position.BottomRight, "Bottom Right  ");
CheckForNodePort(anchor, Position.Bottom,      "Bottom        ");
CheckForNodePort(anchor, Position.BottomLeft,  "Bottom Left   ");

anchor.Port = defaultPort;

}

private void SnapLinksToPorts(NodeAnchor anchor, string anchorText) { log.LogDebug(LoggingType.BlazorEvent, "Link {anchorText} X,Y {X},{Y} ({rX},{rY}).", anchorText, anchor.X, anchor.Y, anchor.RelativeX, anchor.RelativeY);

if (anchor.Node == null) return;

SnapLinkToPort(anchor);

}

// the link modified event private void LinkChanged(LinkBase link) {

if (link != null) {
  if (link.Source != null) SnapLinksToPorts(link.Source, "Source");
  if (link.Target != null) SnapLinksToPorts(link.Target, "Target");
}

LinkNodes(link); // my asynchronous extra work on link modified.

}