Skip to content

Commit

Permalink
Add new built-in connection type: StepConnection (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
miroiu authored Jun 15, 2024
1 parent f5105ae commit ae700b2
Show file tree
Hide file tree
Showing 14 changed files with 376 additions and 22 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

> - Breaking Changes:
> - Features:
> - Added new built-in connection type: StepConnection
> - Bugfixes:
> - Fixed CircuitConnection directional arrows not interpolating correctly
> - Fixed style not applying to the default Connection template outside App.xaml
#### **Version 6.0.0**

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;

namespace Nodify.Playground
{
public class FlowToConnectorPositionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ConnectorViewModel connector)
{
if (connector.Node.Orientation == Orientation.Horizontal)
{
return connector.Flow == ConnectorFlow.Output
? ConnectorPosition.Right
: ConnectorPosition.Left;
}

return connector.Flow == ConnectorFlow.Output
? ConnectorPosition.Bottom
: ConnectorPosition.Top;
}

return value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
12 changes: 12 additions & 0 deletions Examples/Nodify.Playground/Editor/NodifyEditorView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<UserControl.Resources>
<shared:RandomBrushConverter x:Key="RandomBrushConverter" />
<local:FlowToDirectionConverter x:Key="FlowToDirectionConverter" />
<local:FlowToConnectorPositionConverter x:Key="FlowToConnectorPositionConverter" />

<GeometryDrawing x:Key="SmallGridGeometry"
Geometry="M0,0 L0,1 0.03,1 0.03,0.03 1,0.03 1,0 Z"
Expand Down Expand Up @@ -107,6 +108,12 @@
Style="{StaticResource ConnectionStyle}" />
</DataTemplate>

<DataTemplate x:Key="StepConnectionTemplate">
<nodify:StepConnection Style="{StaticResource ConnectionStyle}"
SourcePosition="{Binding Output, Converter={StaticResource FlowToConnectorPositionConverter}}"
TargetPosition="{Binding Input, Converter={StaticResource FlowToConnectorPositionConverter}}" />
</DataTemplate>

<DataTemplate x:Key="ConnectionTemplate">
<nodify:Connection Style="{StaticResource ConnectionStyle}" />
</DataTemplate>
Expand Down Expand Up @@ -204,6 +211,11 @@
<Setter Property="ConnectionTemplate"
Value="{StaticResource CircuitConnectionTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding ConnectionStyle, Mode=TwoWay, Source={x:Static local:EditorSettings.Instance}}"
Value="Step">
<Setter Property="ConnectionTemplate"
Value="{StaticResource StepConnectionTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</nodify:NodifyEditor.Style>
Expand Down
3 changes: 2 additions & 1 deletion Examples/Nodify.Playground/EditorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ public enum ConnectionStyle
{
Default,
Line,
Circuit
Circuit,
Step
}

public class EditorSettings : ObservableObject
Expand Down
22 changes: 4 additions & 18 deletions Nodify/Connections/CircuitConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,27 +65,13 @@ protected override void DrawDirectionalArrowsGeometry(StreamGeometryContext cont
{
var (p0, p1, p2) = GetLinePoints(source, target);

Vector deltaSource = p1 - p0;
Vector deltaTarget = p2 - p1;

double lengthRatio = deltaTarget.LengthSquared / (deltaSource.LengthSquared + deltaTarget.LengthSquared);

int segment1ArrowCount = (int)Math.Round(DirectionalArrowsCount * (1 - lengthRatio));
DrawDirectionalArrowsToSegment(context, p0, p1, segment1ArrowCount);

int segment2ArrowCount = (int)DirectionalArrowsCount - segment1ArrowCount;
DrawDirectionalArrowsToSegment(context, p1, p2, segment2ArrowCount);
}

private void DrawDirectionalArrowsToSegment(StreamGeometryContext context, Point p0, Point p1, int arrowsCount)
{
double spacing = 1d / (arrowsCount + 1);
for (int i = 1; i <= arrowsCount; i++)
double spacing = 1d / (DirectionalArrowsCount + 1);
for (int i = 1; i <= DirectionalArrowsCount; i++)
{
var direction = p0 - p1;
double t = (spacing * i + DirectionalArrowsOffset).WrapToRange(0d, 1d);
var to = InterpolateLineSegment(p0, p1, t);
var (segment, to) = InterpolateLine(p0, p1, p2, t);

var direction = segment.SegmentStart - segment.SegmentEnd;
base.DrawDirectionalArrowheadGeometry(context, direction, to);
}
}
Expand Down
42 changes: 42 additions & 0 deletions Nodify/Connections/LineConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,47 @@ protected static Point InterpolateLineSegment(Point p0, Point p1, double t)
{
return (Point)((1 - t) * (Vector)p0 + t * (Vector)p1);
}

protected static ((Point SegmentStart, Point SegmentEnd), Point InterpolatedPoint) InterpolateLine(Point p0, Point p1, Point p2, Point p3, double t)
{
double length1 = (p1 - p0).Length;
double length2 = (p2 - p1).Length;
double length3 = (p3 - p2).Length;
double totalLength = length1 + length2 + length3;

double ratio1 = length1 / totalLength;
double ratio2 = length2 / totalLength;
double ratio3 = length3 / totalLength;

// Interpolate within the appropriate segment based on t
if (t <= ratio1)
{
return ((p0, p1), InterpolateLineSegment(p0, p1, t / ratio1));
}
else if (t <= ratio1 + ratio2)
{
return ((p1, p2), InterpolateLineSegment(p1, p2, (t - ratio1) / ratio2));
}

return ((p2, p3), InterpolateLineSegment(p2, p3, (t - ratio1 - ratio2) / ratio3));
}

protected static ((Point SegmentStart, Point SegmentEnd), Point InterpolatedPoint) InterpolateLine(Point p0, Point p1, Point p2, double t)
{
double length1 = (p1 - p0).Length;
double length2 = (p2 - p1).Length;
double totalLength = length1 + length2;

double ratio1 = length1 / totalLength;
double ratio2 = length2 / totalLength;

// Interpolate within the appropriate segment based on t
if (t <= ratio1)
{
return ((p0, p1), InterpolateLineSegment(p0, p1, t / ratio1));
}

return ((p1, p2), InterpolateLineSegment(p1, p2, (t - ratio1) / ratio2));
}
}
}
Loading

0 comments on commit ae700b2

Please sign in to comment.