diff --git a/samples/AvalonDraw/MainWindow.axaml b/samples/AvalonDraw/MainWindow.axaml
index 850c437742..54461f007a 100644
--- a/samples/AvalonDraw/MainWindow.axaml
+++ b/samples/AvalonDraw/MainWindow.axaml
@@ -64,6 +64,7 @@
+
diff --git a/samples/AvalonDraw/MainWindow.axaml.cs b/samples/AvalonDraw/MainWindow.axaml.cs
index 23df361bcf..4ba2ca1f76 100644
--- a/samples/AvalonDraw/MainWindow.axaml.cs
+++ b/samples/AvalonDraw/MainWindow.axaml.cs
@@ -2836,6 +2836,29 @@ private void ApplyPathOp(SK.SKPathOp op)
SelectNodeFromElement(result);
}
+ private void BlendSelected()
+ {
+ if (_document is null || _multiSelected.Count != 2)
+ return;
+
+ if (_multiSelected[0] is not SvgPath from || _multiSelected[1] is not SvgPath to)
+ return;
+ if (from.Parent is not SvgElement parent)
+ return;
+
+ SaveUndoState();
+
+ var blends = _pathService.Blend(from, to, 5);
+ var index = parent.Children.IndexOf(to);
+ foreach (var p in blends)
+ {
+ parent.Children.Insert(index++, p);
+ }
+
+ SvgView.SkSvg!.FromSvgDocument(_document);
+ BuildTree();
+ }
+
private void SmoothPointMenuItem_Click(object? sender, RoutedEventArgs e)
{
if (_pathService.IsEditing && _pathService.ActivePoint >= 0 && _document is { })
@@ -2863,6 +2886,7 @@ private void CornerPointMenuItem_Click(object? sender, RoutedEventArgs e)
private void UniteMenuItem_Click(object? sender, RoutedEventArgs e) => ApplyPathOp(SK.SKPathOp.Union);
private void SubtractMenuItem_Click(object? sender, RoutedEventArgs e) => ApplyPathOp(SK.SKPathOp.Difference);
private void IntersectMenuItem_Click(object? sender, RoutedEventArgs e) => ApplyPathOp(SK.SKPathOp.Intersect);
+ private void BlendMenuItem_Click(object? sender, RoutedEventArgs e) => BlendSelected();
private void AlignLeftMenuItem_Click(object? sender, RoutedEventArgs e) => AlignSelected(AlignService.AlignType.Left);
private void AlignHCenterMenuItem_Click(object? sender, RoutedEventArgs e) => AlignSelected(AlignService.AlignType.HCenter);
diff --git a/samples/AvalonDraw/Services/PathService.cs b/samples/AvalonDraw/Services/PathService.cs
index e2e8d6f1a6..3087aecd68 100644
--- a/samples/AvalonDraw/Services/PathService.cs
+++ b/samples/AvalonDraw/Services/PathService.cs
@@ -513,4 +513,75 @@ public static string ToSvgPathData(SK.SKPath skPath)
return path;
}
+
+ private static float Lerp(float a, float b, float t) => a + (b - a) * t;
+
+ private static System.Drawing.PointF Lerp(System.Drawing.PointF a, System.Drawing.PointF b, float t)
+ => new System.Drawing.PointF(Lerp(a.X, b.X, t), Lerp(a.Y, b.Y, t));
+
+ private static SvgPathSegment? LerpSegment(SvgPathSegment s1, SvgPathSegment s2, float t)
+ {
+ if (s1.GetType() != s2.GetType())
+ return null;
+ return s1 switch
+ {
+ SvgMoveToSegment mv1 when s2 is SvgMoveToSegment mv2 => new SvgMoveToSegment(false, Lerp(mv1.End, mv2.End, t)),
+ SvgLineSegment ln1 when s2 is SvgLineSegment ln2 => new SvgLineSegment(false, Lerp(ln1.End, ln2.End, t)),
+ SvgCubicCurveSegment c1 when s2 is SvgCubicCurveSegment c2 => new SvgCubicCurveSegment(false,
+ Lerp(c1.FirstControlPoint, c2.FirstControlPoint, t),
+ Lerp(c1.SecondControlPoint, c2.SecondControlPoint, t),
+ Lerp(c1.End, c2.End, t)),
+ SvgQuadraticCurveSegment q1 when s2 is SvgQuadraticCurveSegment q2 => new SvgQuadraticCurveSegment(false,
+ Lerp(q1.ControlPoint, q2.ControlPoint, t),
+ Lerp(q1.End, q2.End, t)),
+ SvgArcSegment a1 when s2 is SvgArcSegment a2 => new SvgArcSegment(
+ Lerp(a1.RadiusX, a2.RadiusX, t),
+ Lerp(a1.RadiusY, a2.RadiusY, t),
+ Lerp(a1.Angle, a2.Angle, t),
+ a1.Size,
+ a1.Sweep,
+ false,
+ Lerp(a1.End, a2.End, t)),
+ SvgClosePathSegment _ when s2 is SvgClosePathSegment => new SvgClosePathSegment(false),
+ _ => null,
+ };
+ }
+
+ private static SvgPathSegmentList? LerpPath(SvgPathSegmentList from, SvgPathSegmentList to, float t)
+ {
+ if (from.Count != to.Count)
+ return null;
+ var list = new SvgPathSegmentList();
+ for (int i = 0; i < from.Count; i++)
+ {
+ var seg = LerpSegment(from[i], to[i], t);
+ if (seg is null)
+ return null;
+ list.Add(seg);
+ }
+ return list;
+ }
+
+ public IList Blend(SvgPath? from, SvgPath? to, int steps)
+ {
+ var list = new List();
+ if (from is null || to is null || steps <= 0)
+ return list;
+
+ var d1 = from.PathData;
+ var d2 = to.PathData;
+ if (d1 is null || d2 is null)
+ return list;
+
+ for (int i = 1; i <= steps; i++)
+ {
+ var t = i / (float)(steps + 1);
+ var segs = LerpPath(d1, d2, t);
+ if (segs is null)
+ continue;
+ list.Add(new SvgPath { PathData = segs });
+ }
+
+ return list;
+ }
}