Routing problems when rotating node with ToSpot and FromSpot set to whole side

Hello

I’ve been working on rotating some nodes, and have come to a little problem with Node:ToSpot and Node:FromSpot.
I have a node that needs to have different links coming all from one side, differentiating between them. Right now this node has one port set with go:Node.ToSpot=“MiddleLeft”

<DataTemplate x:Key="Mixer">
    <Grid go:Part.SelectionElementName="SpotPanel"
                              MouseLeftButtonDown="Node_MouseLeftButtonDown"
                              go:Part.SelectionAdorned="True"
                              go:Part.SelectionAdornmentTemplate="{StaticResource SelectionHandle}"
                              go:Part.Resizable="False"
                              go:Part.Rotatable="True"
                              go:Node.RotationAngle="{Binding Path=Data.Angle, Mode=TwoWay}"
                              go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
     
                            <go:SpotPanel x:Name="SpotPanel" Grid.Column="1" Grid.Row="0">
                                <ToolTipService.ToolTip>
                                    <TextBlock Text="{Binding Path=Data.Key}" />
                                </ToolTipService.ToolTip>
                                <go:NodePanel>
     
                                    <Path x:Name="Shape"
                                        Data=" F1 M 1,1 C1,1 1,433 1,433 1,433 145,433 145,433 145,433 433,217 433,217 433,217 240,72 240,72 240,72 145,1 145,1 145,1 1,1 1,1 z" 
                                        Width="{Binding Path=Data.Width, Mode=TwoWay}"
                                        Height="{Binding Path=Data.Height, Mode=TwoWay}"
                                        RenderTransformOrigin="0.5,0.5"
                                        Stretch="Fill" 
                                        Fill="#f0f0f0" 
                                        Stroke="#c7c7c7" 
                                        StrokeThickness="2">
                                        <Path.Effect>
                                            <DropShadowEffect/>
                                        </Path.Effect>
                                    </Path>
                                    <Path x:Name="Shape2"
                                        Data="M 73,109 C73,109 73,325 73,325 73,325 253,217 253,217 253,217 73,109 73,109 z" 
                                        Width="{Binding Path=Data.Width, Mode=TwoWay}"
                                        Height="{Binding Path=Data.Height, Mode=TwoWay}"
                                        RenderTransformOrigin="0.5,0.5"
                                        Stretch="Fill" 
                                        Fill="#666" 
                                        Stroke="#666" 
                                        StrokeThickness="2">
     
                                        <Path.LayoutTransform>
                                            <ScaleTransform ScaleX="0.41" ScaleY="0.49"/>
                                        </Path.LayoutTransform>
                                    </Path>
     
                                </go:NodePanel>
                                <Border go:SpotPanel.Spot="1 0.5" 
                                        go:SpotPanel.Alignment="0 0.5"
                                        go:Node.PortId="Out" go:Node.LinkableFrom="True"
                                        go:Node.FromSpot="MiddleRight"
                                        go:Node.LinkableMaximum="1"
                                        Background="Transparent"
                                        Width="8" Height="8">
                                    <Rectangle Width="8" Height="4" Fill="#c7c7c7" />
                                </Border>
                                <Border go:SpotPanel.Spot="0 0.5" 
                                        go:Node.PortId="In" go:Node.LinkableTo="True"
                                        go:Node.ToSpot="LeftSide"
                                        go:Node.LinkableMaximum="{Binding Path=Data.UnitOperation.NumberOfInletsAllowed, Mode=OneWay}"
                                        Background="Transparent"
                                        Width="8" Height="{Binding Path=Data.Height, Mode=OneWay}">
                                    <Rectangle Width="4" Height="{Binding Path=Data.Height, Mode=OneWay}" 
                                               Fill="#c7c7c7" />
                                </Border>
                            </go:SpotPanel>
                            <TextBlock Text="{Binding Path=Data.Key, Mode=TwoWay}" 
                                       Style="{StaticResource TextBlockStyle}"
                                       Grid.Row="1" Grid.ColumnSpan="3" />
                        </Grid>
                    </DataTemplate>

As you can see, the second border (borders are there for easy grabbing) has go:Node.ToSpot=“LeftSide”.
When rotating, the links routing gets all messed up, fusing all connections to a single point.
(I would post images, but it doesn’t let me as a new user.)

I have tried using converters, based on the Angle, like the Piping example.
I tried changing the ToSpot value based on the angle:

public class MixerInletsToSpotDirectionConverter : IValueConverter
    {
 
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Double)
            {
                double angle = (double)value;
                if (angle < 90.0 && angle >= 0.0)
                    return "LeftSide";
                if (angle < 180.0 && angle >= 90.0)
                    return "BottomSide";
                if (angle < 270.0 && angle >= 180.0)
                    return "RightSide";
                if (angle < 360.0 && angle >= 270.0)
                    return "TopSide";
            }
            return "LeftSide";
        }
}

But using it didn’t go as expected, being quite similar to how it was

As I tested, I could see that when linking to the rotated node, the outline of the border port area was still a vertical rectangle, so I tried to move the go:SpotPanel.Spot, using another converter:

public class MixerInletSpotPanelValueConverter : IValueConverter
    {
 
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is Double)
            {
                double angle = (double)value;
                if (angle < 90.0 && angle >= 0.0)
                    return "0 0.5";
                if (angle < 180.0 && angle >= 90.0)
                    return "0.5 0";
                if (angle < 270.0 && angle >= 180.0)
                    return "1 0.5";
                if (angle < 360.0 && angle >= 270.0)
                    return "0.5 1";
            }
            return "0 0.5";
        }
}

This basically did nothing visually.
Then, I god rid of the Border, making the spot directly on the first shape Path. This was worse as almost the whole node because the port “bounding box”.

Before I tried to change the Width and Height with converters too (something that might have failed, as I needed both the angle and the width/height), I rechecked the Pipping example, and make it so the Valve had the ToSpot and FromSpot whole sides, and also got routing problems, just like with my node.

The desired result is of course no routing problems, and multiple streams side by side.

The rotating tool has the following properties changed from default:

RotatingTool.SnapAngleMultiple = 90.0;
RotatingTool.SnapAngleEpsilon = 45.0

;And the LayeredDiagraphLayout has the following attributes

AggressiveOption="Less"
Direction="0"
InitializeOption="DepthFirstOut"
SetsPortSpots="False" 
UseLayoutRounding="False"

Thanks in advance.

EDIT: Adding dropbox folders with some images: Dropbox - Error

Odd, you should be able to insert/upload images, unless they are too big.

I’m not sure exactly what’s going on, but have you set the …EndSegmentDirection on the Link.Route in your Link template(s)?

        <go:Link.Route>
          <go:Route . . .
                    FromEndSegmentDirection="RotatedNodeOrthogonal"
                    ToEndSegmentDirection="RotatedNodeOrthogonal" />
        </go:Link.Route>

The Draggable Link sample uses this, for example. The expectation is that you won’t need to do any fiddling with custom binding of the FromSpot/ToSpot on either Node or Link.

Yep, the template for the link has those values set:

<DataTemplate x:Key="StreamSelectionAdornmentTemplate">
    <go:SelectionHandle go:NodePanel.Figure="None" 
                        Stroke="{DynamicResource AccentColorBrush}" 
                        Fill="{DynamicResource AccentColorBrush}"
                        MouseLeftButtonDown="Node_MouseLeftButtonDown"
                        StrokeThickness="3" 
                        go:Part.Selectable="False" />
</DataTemplate>

<DataTemplate x:Key="StreamReshapeHandleTemplate">
    <go:ToolHandle go:NodePanel.Figure="Diamond" 
                   Width="7" 
                   Height="7" 
                   Fill="Orange" 
                   Stroke="{DynamicResource AccentColorBrush}"
                   StrokeThickness="1" />
</DataTemplate>

<DataTemplate x:Key="StreamTemplate">
    <go:LinkPanel go:Part.SelectionElementName="Shape"
                  MouseLeftButtonDown="Node_MouseLeftButtonDown"
                  go:Part.SelectionAdorned="True"
                  go:Part.SelectionAdornmentTemplate="{StaticResource StreamSelectionAdornmentTemplate}"
                  go:Part.Reshapable="True">
        <ToolTipService.ToolTip>
            <TextBlock Text="{Binding Path=Data.Name}" />
        </ToolTipService.ToolTip>
        <go:Link.Route>
            <go:Route Routing="AvoidsNodes" Curve="JumpOver"
                      RelinkableTo="False" RelinkableFrom="False"
                      FromEndSegmentDirection="RotatedNodeOrthogonal"
                      ToEndSegmentDirection="RotatedNodeOrthogonal"
                      LinkReshapeHandleTemplate="{StaticResource StreamReshapeHandleTemplate}" />
        </go:Link.Route>
        <go:LinkShape Stroke="Transparent"
                      StrokeThickness="10"/>
        <go:LinkShape x:Name="Path"
                      StrokeThickness="2"
                      Stroke="DarkCyan" />
        <Path x:Name="Shape" Fill="DarkCyan" go:LinkPanel.ToArrow="Triangle" />
    </go:LinkPanel>
</DataTemplate>

In fact, I initially based it all on the Draggable Link, even if we decided to forbid lone links.
And yes, it is odd, only happening when the whole side is used for go:Node.ToSpot & go:Node.FromSpot. The DraggableLink example uses “single points”, if I get this correctly (“MiddleBottom”, “MiddleRight”, etc).

I see what you mean when the spot is a Side. We’ll investigate.

FYI, we have fixed this particular problem, but additional problems remain or have been introduced. So we’re not ready for a bug fix release…

Thanks for the update.
That’s both good (it was fixed) and bad (that it introduced side effects) to hear. Right now the feature was moved down on priority here, as we now have to decide whether to wait for a bugfix release, or change the way these nodes are constructed, maybe with runtime creation of ports. As this would pose more complexity, onto the backlog it goes for now.