///
/// DDA算法实现直线插补
///
public class LineInterpolator
{
private PointF _currentPoint;
private PointF _endPoint;
private float _stepSize;
private float _dx, _dy;
private float _xIncrement, _yIncrement;
public LineInterpolator(PointF start, PointF end, float stepSize)
{
_currentPoint = start;
_endPoint = end;
_stepSize = stepSize;
_dx = end.X - start.X;
_dy = end.Y - start.Y;
// 计算每步增量
float steps = Math.Max(Math.Abs(_dx), Math.Abs(_dy)) / _stepSize;
_xIncrement = _dx / steps;
_yIncrement = _dy / steps;
}
public bool MoveNext()
{
if (IsEndReached()) return false;
// 更新坐标(防溢出处理)
_currentPoint.X += _xIncrement;
_currentPoint.Y += _yIncrement;
// 硬件级精度补偿
if (Math.Abs(_currentPoint.X - _endPoint.X) < _stepSize * 0.5f)
_currentPoint.X = _endPoint.X;
if (Math.Abs(_currentPoint.Y - _endPoint.Y) < _stepSize * 0.5f)
_currentPoint.Y = _endPoint.Y;
return true;
}
public PointF Current => _currentPoint;
private bool IsEndReached()
{
return
Math.Abs(_currentPoint.X - _endPoint.X) < _stepSize * 0.5f &&
Math.Abs(_currentPoint.Y - _endPoint.Y) < _stepSize * 0.5f;
}
}
关键点:
stepSize
调节动画流畅度///
/// Bresenham算法实现整数运算优化
///
public class BresenhamInterpolator
{
private int _x, _y;
private readonly int _xEnd, _yEnd;
private readonly int _dx, _dy;
private readonly int _step;
private readonly int _twoDy, _twoDyMinusDx;
public BresenhamInterpolator(int x0, int y0, int x1, int y1)
{
_x = x0;
_y = y0;
_xEnd = x1;
_yEnd = y1;
_dx = Math.Abs(x1 - x0);
_dy = Math.Abs(y1 - y0);
_twoDy = 2 * _dy;
_twoDyMinusDx = 2 * (_dy - _dx);
_step = 1;
}
public bool MoveNext()
{
if (_x == _xEnd && _y == _yEnd) return false;
int decision = _twoDy - _dx;
if (decision >= 0)
{
_y += _step;
_twoDy -= 2 * _dx;
}
_x += _step;
_twoDy -= 2 * _dx;
return true;
}
public (int x, int y) Current => (_x, _y);
}
优势:
<Window x:Class="LineInterpolator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="直线插补动画演示" Height="600" Width="800">
<Grid>
<Canvas Name="DrawingCanvas" Background="White"/>
</Grid>
</Window>
public partial class MainWindow : Window
{
private DispatcherTimer _timer;
private List<LineInterpolator> _interpolators = new();
private readonly Random _rand = new();
public MainWindow()
{
InitializeComponent();
InitializeAnimation();
}
private void InitializeAnimation()
{
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) };
_timer.Tick += Timer_Tick;
_timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
DrawingCanvas.Children.Clear();
foreach (var interpolator in _interpolators)
{
if (interpolator.MoveNext())
{
var line = new Line
{
X1 = interpolator.Current.X,
Y1 = interpolator.Current.Y,
X2 = interpolator.Current.X + 1, // 单像素尾迹
Y2 = interpolator.Current.Y + 1,
Stroke = Brushes.Red,
StrokeThickness = 2
};
DrawingCanvas.Children.Add(line);
}
else
{
_interpolators.Remove(interpolator);
}
}
}
// 触发新插补路径
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var startPoint = e.GetPosition(DrawingCanvas);
var endPoint = new Point(
_rand.Next((int)DrawingCanvas.ActualWidth),
_rand.Next((int)DrawingCanvas.ActualHeight));
_interpolators.Add(new LineInterpolator(
(PointF)startPoint,
(PointF)endPoint,
stepSize: 0.5f));
}
}
关键特性:
DispatcherTimer
实现60fps流畅度// 八边形坐标生成器
public static class PolygonGenerator
{
public static List<PointF> GenerateOctagon(float centerX, float centerY, float radius)
{
var points = new List<PointF>();
for (int i = 0; i < 8; i++)
{
float angle = (i * MathF.PI / 4) - MathF.PI / 8;
float x = (float)(centerX + radius * Math.Cos(angle));
float y = (float)(centerY + radius * Math.Sin(angle));
points.Add(new PointF(x, y));
}
return points;
}
}
// 动态绘制八边形
private void DrawOctagon()
{
var points = PolygonGenerator.GenerateOctagon(
centerX: (float)DrawingCanvas.ActualWidth / 2,
centerY: (float)DrawingCanvas.ActualHeight / 2,
radius: 200);
// 创建连续插补路径
for (int i = 0; i < points.Count; i++)
{
var start = points[i];
var end = points[(i + 1) % points.Count];
_interpolators.Add(new LineInterpolator(start, end, stepSize: 0.5f));
}
}
// 启用WPF硬件加速
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
// 使用WriteableBitmap优化绘制
private WriteableBitmap _bitmap;
private byte[] _pixelData;
public MainWindow()
{
InitializeComponent();
_bitmap = new WriteableBitmap(
(int)DrawingCanvas.ActualWidth,
(int)DrawingCanvas.ActualHeight,
96, 96, PixelFormats.Bgr32, null);
DrawingCanvas.Children.Add(new Image { Source = _bitmap });
_pixelData = new byte[_bitmap.PixelWidth * _bitmap.PixelHeight * 4];
}
private void Timer_Tick(...)
{
_bitmap.Lock();
for (int i = 0; i < _pixelData.Length; i += 4)
{
_pixelData[i] = 255; // B
_pixelData[i + 1] = 255; // G
_pixelData[i + 2] = 255; // R
_pixelData[i + 3] = 255; // A
}
foreach (var interpolator in _interpolators)
{
var point = interpolator.Current;
int index = (int)(point.Y * _bitmap.PixelWidth + point.X) * 4;
_pixelData[index] = 0; // B
_pixelData[index + 1] = 0; // G
_pixelData[index + 2] = 255; // R
}
_bitmap.WritePixels(
new Int32Rect(0, 0, _bitmap.PixelWidth, _bitmap.PixelHeight),
_pixelData,
_bitmap.PixelWidth * 4,
0);
_bitmap.Unlock();
}
性能提升:
// 异步路径计算
private async void Window_MouseLeftButtonDown(...)
{
await Task.Run(() =>
{
var interpolator = new LineInterpolator(...);
while (interpolator.MoveNext())
{
// 后台计算坐标
}
}).ContinueWith(t => Dispatcher.Invoke(() =>
_interpolators.Add(interpolator)),
TaskScheduler.FromCurrentSynchronizationContext());
}
// 风车路径生成器
public static class WindmillGenerator
{
public static List<PointF> GenerateWindmill(float centerX, float centerY, float radius)
{
var points = new List<PointF>();
// 直线段
points.Add(new PointF(centerX + radius, centerY));
points.Add(new PointF(centerX - radius, centerY));
// 圆弧段
for (int i = 0; i < 4; i++)
{
float angle = i * MathF.PI / 2;
points.Add(new PointF(
(float)(centerX + radius * Math.Cos(angle)),
(float)(centerY + radius * Math.Sin(angle))));
}
return points;
}
}
// 模拟雷赛控制卡通信
public class MotionController
{
private const int Acceleration = 1000;
private const int MaxSpeed = 5000;
public void StartInterpolation(List<PointF> path)
{
foreach (var point in path)
{
// 调用SDK函数
// ltdmc.dmc_line_multicoor(...);
// 省略通信代码
}
}
}
通过本文的深度实践,开发者可以实现: