187 lines
5.5 KiB
C#
187 lines
5.5 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.Media;
|
|
using Avalonia.Threading;
|
|
using Masuit.Tools;
|
|
|
|
namespace DispenserUI.Views.Controls;
|
|
|
|
public partial class CoordinateViewer : UserControl
|
|
{
|
|
public static readonly StyledProperty<double[][]> CoordsProperty =
|
|
AvaloniaProperty.Register<CoordinateViewer,
|
|
double[][]>(nameof(Coords));
|
|
|
|
public static readonly StyledProperty<double[][]> PathProperty =
|
|
AvaloniaProperty.Register<CoordinateViewer,
|
|
double[][]>(nameof(Path));
|
|
|
|
public static readonly StyledProperty<bool> RotateProperty =
|
|
AvaloniaProperty.Register<CoordinateViewer,
|
|
bool>(nameof(Rotate));
|
|
|
|
private readonly object _lock = new();
|
|
|
|
public CoordinateViewer()
|
|
{
|
|
InitializeComponent();
|
|
this.GetObservable(CoordsProperty).Throttle(TimeSpan.FromMilliseconds(1000))
|
|
.Subscribe(val =>
|
|
{
|
|
if (val.IsNullOrEmpty()) return;
|
|
Dispatcher.UIThread.InvokeAsync(InvalidateVisual);
|
|
});
|
|
this.GetObservable(PathProperty).Throttle(TimeSpan.FromSeconds(1))
|
|
.Subscribe(val =>
|
|
{
|
|
if (val.IsNullOrEmpty()) return;
|
|
Dispatcher.UIThread.InvokeAsync(InvalidateVisual);
|
|
});
|
|
}
|
|
|
|
private void InitializeComponent()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
}
|
|
|
|
public double[][] Coords
|
|
{
|
|
get => GetValue(CoordsProperty);
|
|
set => SetValue(CoordsProperty, value);
|
|
}
|
|
|
|
public double[][] Path
|
|
{
|
|
get => GetValue(PathProperty);
|
|
set => SetValue(PathProperty, value);
|
|
}
|
|
|
|
public bool Rotate
|
|
{
|
|
get => GetValue(RotateProperty);
|
|
set => SetValue(RotateProperty, value);
|
|
}
|
|
|
|
public override void Render(DrawingContext context)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
base.Render(context);
|
|
|
|
if (Coords.IsNullOrEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var coords = CloneCoords(Coords);
|
|
|
|
var boundsWidth = DesiredSize.Width;
|
|
var boundsHeight = DesiredSize.Height;
|
|
|
|
if (Rotate)
|
|
{
|
|
// 将坐标进行旋转90°
|
|
Rotate90Degrees(coords);
|
|
}
|
|
|
|
// 将坐标进行Y轴翻转
|
|
var points = coords.Select(point => new Point(-point[0], point[1])).ToList();
|
|
|
|
// 找到坐标中最小的X、Y坐标
|
|
var minX = points.Min(point => point.X);
|
|
// 查找最小的Y
|
|
var minY = points.Min(point => point.Y);
|
|
// 找到最大的X
|
|
var maxX = points.Max(point => point.X);
|
|
// 找到最大的Y
|
|
var maxY = points.Max(point => point.Y);
|
|
|
|
var aspectRatio = (maxY - minY) / (maxX - minX);
|
|
|
|
// 更改缩放比例,使得容器的宽高比尽量接近坐标的宽高比
|
|
var scaleBoundsWidth = boundsHeight / aspectRatio;
|
|
|
|
var scaleX = scaleBoundsWidth / (maxX - minX);
|
|
var scaleY = boundsHeight / (maxY - minY);
|
|
|
|
var offsetX = (boundsWidth - (maxX - minX) * scaleX) / 2;
|
|
|
|
|
|
foreach (var point in points)
|
|
{
|
|
// 将X 进行居中对齐
|
|
var x = (point.X - minX) * scaleX + offsetX;
|
|
var y = (point.Y - minY) * scaleY;
|
|
|
|
|
|
var rect = new Rect(x, y, 2, 5);
|
|
|
|
var pen = new Pen(Brushes.Black, 0.5);
|
|
context.DrawRectangle(pen, rect);
|
|
}
|
|
|
|
|
|
// 如果路径不为空的话,绘制路径
|
|
if (Path.IsNullOrEmpty()) return;
|
|
|
|
// 计算出路径的坐标点
|
|
var paths = Path
|
|
.Select(point => new Point((-point[0] - minX) * scaleX + offsetX, (point[1] - minY) * scaleY)).ToList();
|
|
|
|
var geometry = new StreamGeometry();
|
|
|
|
using (var ctx = geometry.Open())
|
|
{
|
|
ctx.BeginFigure(paths[0], false);
|
|
foreach (var point in paths.Skip(1))
|
|
{
|
|
ctx.LineTo(point);
|
|
}
|
|
|
|
ctx.EndFigure(false);
|
|
}
|
|
|
|
// 绘制路径
|
|
context.DrawGeometry(null, new Pen(Brushes.Red, 1), geometry);
|
|
|
|
// 在开始的第一个点标注为绿色
|
|
var startPoint = paths[0];
|
|
context.DrawEllipse(new SolidColorBrush(Colors.Green), new Pen(Brushes.Green, 1),
|
|
new Rect(new Point(startPoint.X - 2, startPoint.Y - 2), new Size(5, 5)));
|
|
|
|
// 最后一个点标注为黄色
|
|
var endPoint = paths[^1];
|
|
context.DrawEllipse(new SolidColorBrush(Colors.Yellow), new Pen(Brushes.Yellow, 1),
|
|
new Rect(new Point(endPoint.X - 2, endPoint.Y - 2), new Size(5, 5)));
|
|
}
|
|
}
|
|
|
|
private static void Rotate90Degrees(double[][] coords)
|
|
{
|
|
foreach (var coord in coords)
|
|
{
|
|
var tempX = coord[0];
|
|
var tempY = coord[1];
|
|
coord[0] = -tempY;
|
|
coord[1] = tempX;
|
|
}
|
|
}
|
|
|
|
private static double[][] CloneCoords(double[][] original)
|
|
{
|
|
var copy = new double[original.Length][];
|
|
|
|
// 逐行复制
|
|
for (var i = 0; i < original.Length; i++)
|
|
{
|
|
copy[i] = new double[original[i].Length];
|
|
Array.Copy(original[i], copy[i], original[i].Length);
|
|
}
|
|
|
|
return copy;
|
|
}
|
|
} |