1341 lines
47 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AiSportsMicrospaceDB.DBContext;
using AiSportsMicrospaceDB.Entities;
using Common;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Yztob.AiSports.Sensors.Abstractions;
using Yztob.AiSports.Sensors.Things;
using Line = System.Windows.Shapes.Line;
using Point = System.Windows.Point;
namespace Wpf_AiSportsMicrospace.Views
{
/// <summary>
/// 多人大绳点位配置页面
/// 功能:
/// 1. 根据 HomeWidth 动态生成 5-7 个点位
/// 2. 支持 Y 轴拖动调整
/// 3. 从数据库加载/保存点位数据
/// </summary>
public partial class MultiJumpRope : UserControl
{
#region 私有变量
private bool _isDragging = false;
private Ellipse _draggedEllipse;
private double _dragStartY;
private double _originalY;
private double _dragStartX;
private double _originalX;
/// <summary>
/// 点位数据列表
/// </summary>
private List<JumpRopePoint> _points = new List<JumpRopePoint>();
/// <summary>
/// 当前场地宽度
/// </summary>
private double _homeWidth = 6.0;
/// <summary>
/// 点位半径(长轴)
/// </summary>
private const double PointRadiusY = 35;
/// <summary>
/// 点位短轴(扁平化比例 0.5)
/// </summary>
private const double PointRadiusX = 70;
private int _lastFrameNumber = -1;
private WriteableBitmap _videoBitmap;
/// <summary>
/// 重要点位索引(0 表示不设置)
/// </summary>
private int _keyPointIndex = 0;
/// <summary>
/// 音乐跳绳二号位索引(0 表示不设置)
/// </summary>
private int _musicPoint2Index = 0;
#endregion
#region 构造函数
public MultiJumpRope()
{
InitializeComponent();
SetupDragEvents();
}
#endregion
#region 私有方法
/// <summary>
/// 页面加载完成事件
/// </summary>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadPointsFromDatabase();
}
/// <summary>
/// 设置拖拽事件
/// </summary>
private void SetupDragEvents()
{
MainCanvas.MouseMove += MainCanvas_MouseMove;
MainCanvas.MouseLeftButtonUp += MainCanvas_MouseLeftButtonUp;
MainCanvas.MouseLeave += MainCanvas_MouseLeave;
}
/// <summary>
/// 根据 HomeWidth 计算点位数量
/// HomeWidth <= 5:5 个点位
/// 5 < HomeWidth < 7:6 个点位
/// HomeWidth >= 7:7 个点位
/// </summary>
private int CalculatePointCount(double homeWidth)
{
if (homeWidth <= 5)
return 5;
else if (homeWidth > 5 && homeWidth < 7)
return 6;
else
return 7;
}
/// <summary>
/// 从数据库加载点位数据
/// </summary>
private async void LoadPointsFromDatabase()
{
try
{
var app = (App)Application.Current;
using var scope = App.AppHost.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// 获取场地宽度配置
var baseConfig = await db.BasicConfig.FirstOrDefaultAsync(x => x.Code == AppSettings.Code);
if (baseConfig == null)
{
UpdateStatus("未找到相机主机配置");
return;
}
_homeWidth = baseConfig.HomeWidth;
//int pointCount = CalculatePointCount(_homeWidth);
int pointCount = (int)(_homeWidth);
// 更新 UI 显示
//HomeWidthText.Text = _homeWidth.ToString("F1");
PointCountText.Text = $"点位数量: {pointCount} 个";
// 清空现有控件
ClearPoints();
// 获取已保存的点位数据
var pointsData = await db.GroupPoints.FirstOrDefaultAsync(x => x.Code == AppSettings.Code);
if (pointsData != null)
{
// 从数据库加载保存的点位
LoadPointsFromDatabaseData(pointsData, pointCount);
}
else
{
// 生成默认点位位置
CreateDefaultPoints(pointCount);
}
var BaseData = await db.BasicConfig.FirstOrDefaultAsync(x => x.Code == AppSettings.Code);
if (BaseData == null)
{
this.ShowError("相机主机地址/IP不能为空。");
return;
}
var _webcamClient = WebcamClient.CreateRTSP(BaseData.Host, BaseData.UserName, BaseData.Password);
//处理抽帧回调
_webcamClient.OnExtractFrame += this.OnFrameExtracted;
_webcamClient.StartExtract();//开始抽帧
UpdateStatus($"点数据加载成功 - 共 {pointCount} 个点位");
}
catch (Exception ex)
{
UpdateStatus($"加载点数据失败: {ex.Message}");
}
}
private async void OnFrameExtracted(VideoFrame frame)
{
if (frame == null) return;
// 跳帧:每秒只渲染 10~15 帧,降低 CPU 占用
if (_lastFrameNumber != -1 && frame.Number - _lastFrameNumber < 2) return;
_lastFrameNumber = (int)frame.Number;
//获得帧二进制流,保存图像、人体识别竺
var buffer = frame.GetImageBuffer(Yztob.AiSports.Sensors.Things.ImageFormat.Jpeg).ToArray();
//await File.WriteAllBytesAsync($"./temps/{frame.Number}.jpg", buffer);
// === 显示到 WPF ===
BitmapImage bitmap = null;
await Task.Run(() =>
{
using var ms = new MemoryStream(buffer);
bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = ms;
bitmap.EndInit();
bitmap.Freeze(); // 跨线程安全
});
// UI 线程显示
Application.Current.Dispatcher.Invoke(() =>
{
if (videoImage == null) return;
if (_videoBitmap == null ||
_videoBitmap.PixelWidth != bitmap.PixelWidth ||
_videoBitmap.PixelHeight != bitmap.PixelHeight)
{
_videoBitmap = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight,
96, 96, PixelFormats.Bgra32, null);
videoImage.Source = _videoBitmap;
}
_videoBitmap.Lock();
bitmap.CopyPixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight),
_videoBitmap.BackBuffer,
_videoBitmap.BackBufferStride * bitmap.PixelHeight,
_videoBitmap.BackBufferStride);
_videoBitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
_videoBitmap.Unlock();
});
}
/// <summary>
/// 清空所有点位控件
/// </summary>
private void ClearPoints()
{
_points.Clear();
PointsContainer.ItemsSource = null;
ConnectionLine.Points.Clear();
// 清除画布上除了背景和固定控件外的所有元素
var elementsToRemove = new List<UIElement>();
foreach (var child in MainCanvas.Children)
{
if (child is Ellipse || child is Border || child is Line)
{
var element = child as FrameworkElement;
if (element?.Name != "ConnectionLine" &&
element?.Name != "PointsContainer" &&
element?.Name != "CoordinateDisplay")
{
elementsToRemove.Add(element);
}
}
}
foreach (var element in elementsToRemove)
{
MainCanvas.Children.Remove(element);
}
}
/// <summary>
/// 从数据库数据加载点位
/// db.GroupPoints 包含 Point1_X, Point1_Y, Point2_X, Point2_Y 等字段
/// </summary>
private void LoadPointsFromDatabaseData(GroupPoints data, int pointCount)
{
for (int i = 1; i <= pointCount; i++)
{
var point = new JumpRopePoint
{
Id = i,
Index = i
};
// 从数据库读取 Point 类型的属性
var pointProperty = typeof(GroupPoints).GetProperty($"Point{i}");
if (pointProperty != null)
{
var dbPoint = pointProperty.GetValue(data);
if (dbPoint != null)
{
// 获取 Point 的 X 和 Y 属性
var xProp = dbPoint.GetType().GetProperty("X");
var yProp = dbPoint.GetType().GetProperty("Y");
if (xProp != null && yProp != null)
{
point.X = (double)(xProp.GetValue(dbPoint) ?? 0);
point.Y = (double)(yProp.GetValue(dbPoint) ?? 0);
}
}
}
// 如果坐标为 0,则使用默认值
if (point.X == 0) point.X = GetDefaultX(i, pointCount);
if (point.Y == 0) point.Y = GetDefaultY();
point.BackgroundColor = i % 2 == 0 ? "#F3F4F6" : "#FFFFFF";
_points.Add(point);
CreatePointControl(point);
}
// 加载控制点位索引
var keyPointProperty = typeof(GroupPoints).GetProperty("KeyPointIndex");
if (keyPointProperty != null)
{
_keyPointIndex = (int)(keyPointProperty.GetValue(data) ?? 0);
}
else
{
_keyPointIndex = 0;
}
// 加载音乐跳绳二号位位置(从 Point10 字段),并与点位列表对比
var point10Property = typeof(GroupPoints).GetProperty("Point10");
if (point10Property != null)
{
var dbPoint10 = point10Property.GetValue(data);
if (dbPoint10 != null)
{
var xProp = dbPoint10.GetType().GetProperty("X");
var yProp = dbPoint10.GetType().GetProperty("Y");
if (xProp != null && yProp != null)
{
var point10X = (double)(xProp.GetValue(dbPoint10) ?? 0);
var point10Y = (double)(yProp.GetValue(dbPoint10) ?? 0);
// 与点位列表对比,找出匹配的点位
double tolerance = 1.0; // 容差值,允许1像素的误差
foreach (var point in _points)
{
if (Math.Abs(point.X - point10X) < tolerance &&
Math.Abs(point.Y - point10Y) < tolerance)
{
_musicPoint2Index = point.Id;
UpdateStatus($"音乐跳绳二号位已识别为点位 {point.Id}: X:{point10X:F0} Y:{point10Y:F0}");
break;
}
}
}
}
}
RefreshPointsDataList();
UpdateConnectionLine();
RefreshKeyPointComboBox();
}
/// <summary>
/// 创建默认点位
/// </summary>
private void CreateDefaultPoints(int pointCount)
{
for (int i = 1; i <= pointCount; i++)
{
var point = new JumpRopePoint
{
Id = i,
Index = i,
X = GetDefaultX(i, pointCount),
Y = GetDefaultY(),
BackgroundColor = i % 2 == 0 ? "#F3F4F6" : "#FFFFFF"
};
_points.Add(point);
CreatePointControl(point);
}
RefreshPointsDataList();
UpdateConnectionLine();
RefreshKeyPointComboBox();
}
/// <summary>
/// 获取默认 X 坐标(均匀分布)
/// </summary>
private double GetDefaultX(int index, int pointCount)
{
double canvasWidth = MainCanvas.ActualWidth > 0 ? MainCanvas.ActualWidth : 1920;
// 小边距,让第一个点和最后一个点靠近边缘
double margin = 90;
double availableWidth = canvasWidth - (margin * 2);
// 均匀分布在 margin 和 canvasWidth - margin 之间
if (pointCount > 1)
{
double spacing = availableWidth / (pointCount - 1);
return margin + (spacing * (index - 1));
}
return canvasWidth / 2;
}
/// <summary>
/// 获取默认 Y 坐标(画布中间)
/// </summary>
private double GetDefaultY()
{
double canvasHeight = MainCanvas.ActualHeight > 0 ? MainCanvas.ActualHeight : 800;
return canvasHeight / 2;
}
/// <summary>
/// 创建点位控件
/// </summary>
private void CreatePointControl(JumpRopePoint point)
{
// 创建外圈光晕
var glow = new Ellipse
{
Width = (PointRadiusX + 10) * 2,
Height = (PointRadiusY + 10) * 2,
Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#3434D399")),
Opacity = 0.5
};
Canvas.SetLeft(glow, point.X - glow.Width / 2);
Canvas.SetTop(glow, point.Y - glow.Height / 2);
Canvas.SetZIndex(glow, 1);
// 创建主椭圆
var ellipse = new Ellipse
{
Name = $"PointEllipse_{point.Id}",
Width = PointRadiusX * 2,
Height = PointRadiusY * 2,
Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#34D399"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#059669"), 1)
}
},
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#059669")),
StrokeThickness = 2,
//Cursor = Cursors.SizeNS // 垂直方向光标
Cursor = Cursors.SizeAll
};
// 绑定数据
ellipse.DataContext = point;
ellipse.Tag = point;
// 设置位置
Canvas.SetLeft(ellipse, point.X - PointRadiusX);
Canvas.SetTop(ellipse, point.Y - PointRadiusY);
Canvas.SetZIndex(ellipse, 10);
// 添加鼠标事件
ellipse.MouseLeftButtonDown += Point_MouseLeftButtonDown;
ellipse.MouseMove += Point_MouseMove; // 新增
ellipse.MouseLeftButtonUp += Point_MouseLeftButtonUp; // 新增
ellipse.MouseEnter += Point_MouseEnter;
ellipse.MouseLeave += Point_MouseLeave;
// 创建标签
var label = new TextBlock
{
Text = $"P{point.Id}",
Foreground = Brushes.White,
FontSize = 16,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
var labelBorder = new Border
{
Child = label,
Name = $"label{point.Id}",
Width = PointRadiusX * 2,
Height = PointRadiusY * 2,
Background = Brushes.Transparent,
IsHitTestVisible = false // 让鼠标事件穿透到下层的椭圆
};
Canvas.SetLeft(labelBorder, point.X - PointRadiusX);
Canvas.SetTop(labelBorder, point.Y - PointRadiusY);
Canvas.SetZIndex(labelBorder, 11);
// 创建 X 轴引导线(垂直虚线)
var xGuideLine = new Line
{
Name = $"XGuideLine_{point.Id}",
X1 = point.X,
Y1 = 0,
X2 = point.X,
Y2 = MainCanvas.ActualHeight > 0 ? MainCanvas.ActualHeight : 800,
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#6366F1")),
StrokeThickness = 1,
StrokeDashArray = new DoubleCollection { 5, 5 },
Opacity = 0.4
};
Canvas.SetZIndex(xGuideLine, 2);
// 创建 Y 轴引导线(水平虚线)
var yGuideLine = new Line
{
Name = $"YGuideLine_{point.Id}",
X1 = 0,
Y1 = point.Y,
X2 = MainCanvas.ActualWidth > 0 ? MainCanvas.ActualWidth : 1920,
Y2 = point.Y,
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#10B981")),
StrokeThickness = 1,
StrokeDashArray = new DoubleCollection { 5, 5 },
Opacity = 0.4
};
Canvas.SetZIndex(yGuideLine, 2);
// 添加到画布
MainCanvas.Children.Add(glow);
MainCanvas.Children.Add(xGuideLine);
MainCanvas.Children.Add(yGuideLine);
MainCanvas.Children.Add(ellipse);
MainCanvas.Children.Add(labelBorder);
}
/// <summary>
/// 更新右侧点位数据列表
/// </summary>
private void RefreshPointsDataList()
{
var items = _points.Select((point, index) => new
{
PointLabel = point.Id.ToString(),
CoordinateText = $"X:{Math.Round(point.X)} Y:{Math.Round(point.Y)}",
BackgroundColor = point.BackgroundColor
}).ToList();
PointsDataList.ItemsSource = items;
}
/// <summary>
/// 刷新控制点位下拉框
/// </summary>
private void RefreshKeyPointComboBox()
{
// 保存当前选择
int previousSelection = KeyPointComboBox.SelectedIndex;
int previousSelection2 = MusicPointBox2.SelectedIndex;
// 清除并重新填充选项
KeyPointComboBox.Items.Clear();
MusicPointBox2.Items.Clear();
// 为每个 ComboBox 创建独立的 "不设置" 选项
var noneItem1 = new ComboBoxItem { Content = "不设置", Tag = 0 };
var noneItem2 = new ComboBoxItem { Content = "不设置", Tag = 0 };
var noneItem3 = new ComboBoxItem { Content = "不设置", Tag = 0 };
KeyPointComboBox.Items.Add(noneItem1);
MusicPointBox2.Items.Add(noneItem3);
// 添加所有点位选项(为每个 ComboBox 创建独立实例)
foreach (var point in _points)
{
var item1 = new ComboBoxItem { Content = $"点位 {point.Id}", Tag = point.Id };
var item2 = new ComboBoxItem { Content = $"点位 {point.Id}", Tag = point.Id };
var item3 = new ComboBoxItem { Content = $"点位 {point.Id}", Tag = point.Id };
KeyPointComboBox.Items.Add(item1);
MusicPointBox2.Items.Add(item3);
}
// 恢复选择或默认选择
if (previousSelection > 0 && previousSelection <= _points.Count)
{
KeyPointComboBox.SelectedIndex = previousSelection;
}
else if (_keyPointIndex > 0)
{
// 恢复保存的控制点位选择
int comboIndex = _points.FindIndex(p => p.Id == _keyPointIndex);
if (comboIndex >= 0)
{
KeyPointComboBox.SelectedIndex = comboIndex + 1; // +1 因为第一个是"不设置"
}
else
{
KeyPointComboBox.SelectedIndex = 0;
}
}
else
{
KeyPointComboBox.SelectedIndex = 0;
}
// 恢复音乐跳绳二号位选择
if (previousSelection2 > 0 && previousSelection2 <= _points.Count)
{
MusicPointBox2.SelectedIndex = previousSelection2;
}
else if (_musicPoint2Index > 0)
{
int comboIndex = _points.FindIndex(p => p.Id == _musicPoint2Index);
if (comboIndex >= 0)
{
MusicPointBox2.SelectedIndex = comboIndex + 1;
}
else
{
MusicPointBox2.SelectedIndex = 0;
}
}
else
{
MusicPointBox2.SelectedIndex = 0;
}
}
/// <summary>
/// 控制点位选择改变事件
/// </summary>
private void KeyPointComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (KeyPointComboBox.SelectedItem is ComboBoxItem selectedItem)
{
_keyPointIndex = (int)(selectedItem.Tag ?? 0);
if (_keyPointIndex > 0)
{
UpdateStatus($"已设置点位 {_keyPointIndex} 为控制点位");
HighlightKeyPoint(_keyPointIndex);
}
else
{
UpdateStatus("已取消控制点位设置");
ResetAllPointsHighlight();
}
}
}
/// <summary>
/// 音乐跳绳二号位选择改变事件
/// </summary>
private void MusicPointBox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (MusicPointBox2.SelectedItem is ComboBoxItem selectedItem)
{
_musicPoint2Index = (int)(selectedItem.Tag ?? 0);
if (_musicPoint2Index > 0)
{
UpdateStatus($"已设置点位 {_musicPoint2Index} 为音乐跳绳二号位");
HighlightMusicPoint2(_musicPoint2Index);
}
else
{
UpdateStatus("已取消音乐跳绳二号位设置");
ResetAllPointsHighlight();
}
}
}
/// <summary>
/// 高亮显示控制点位
/// </summary>
private void HighlightKeyPoint(int pointId)
{
// 重置所有点位样式
ResetAllPointsHighlight();
// 高亮控制点位
var ellipse = MainCanvas.Children
.OfType<Ellipse>()
.FirstOrDefault(e => e.Name == $"PointEllipse_{pointId}");
if (ellipse != null)
{
ellipse.Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#EF4444"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#DC2626"), 1)
}
};
ellipse.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#B91C1C"));
ellipse.StrokeThickness = 3;
}
}
/// <summary>
/// 高亮显示音乐跳绳二号位
/// </summary>
private void HighlightMusicPoint2(int pointId)
{
// 重置所有点位样式
ResetAllPointsHighlight();
// 高亮音乐跳绳二号位(使用紫色区分)
var ellipse = MainCanvas.Children
.OfType<Ellipse>()
.FirstOrDefault(e => e.Name == $"PointEllipse_{pointId}");
if (ellipse != null)
{
ellipse.Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#8B5CF6"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#7C3AED"), 1)
}
};
ellipse.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#6D28D9"));
ellipse.StrokeThickness = 3;
}
}
/// <summary>
/// 重置所有点位的高亮样式
/// </summary>
private void ResetAllPointsHighlight()
{
foreach (var point in _points)
{
var ellipse = MainCanvas.Children
.OfType<Ellipse>()
.FirstOrDefault(e => e.Name == $"PointEllipse_{point.Id}");
if (ellipse != null)
{
ellipse.Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#34D399"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#059669"), 1)
}
};
ellipse.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#059669"));
ellipse.StrokeThickness = 2;
}
}
}
/// <summary>
/// 更新连接线
/// </summary>
private void UpdateConnectionLine()
{
var points = new PointCollection();
foreach (var point in _points.OrderBy(p => p.X))
{
points.Add(new Point(point.X, point.Y));
}
ConnectionLine.Points = points;
}
#endregion
#region 鼠标事件处理
/// <summary>
/// 点位鼠标按下事件
/// </summary>
private void Point_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isDragging = true;
_draggedEllipse = sender as Ellipse;
//_dragStartY = e.GetPosition(MainCanvas).Y;
//if (_draggedEllipse.Tag is JumpRopePoint point)
//{
// _originalY = point.Y;
//}
// 现在:同时捕获X和Y坐标
var currentPos = e.GetPosition(MainCanvas);
_dragStartX = currentPos.X;
_dragStartY = currentPos.Y;
if (_draggedEllipse.Tag is JumpRopePoint point)
{
_originalX = point.X;
_originalY = point.Y;
}
_draggedEllipse.CaptureMouse();
// 提升 Z 索引
Canvas.SetZIndex(_draggedEllipse, 100);
// 改变颜色表示选中状态
_draggedEllipse.Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#FBBF24"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#D97706"), 1)
}
};
_draggedEllipse.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#D97706"));
UpdateStatus($"正在调整 P{(_draggedEllipse.Tag as JumpRopePoint)?.Id} 点的 Y 坐标");
}
/// <summary>
/// 点位鼠标移动事件(在椭圆上直接处理)
/// </summary>
private void Point_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging && _draggedEllipse != null)
{
var currentPos = e.GetPosition(MainCanvas);
var deltaY = currentPos.Y - _dragStartY;
var deltaX = currentPos.X - _dragStartX;
if (_draggedEllipse.Tag is JumpRopePoint point)
{
// 同时计算X和Y轴移动
double newY = _originalY + deltaY;
double newX = _originalX + deltaX;
// 限制在画布范围内
double canvasHeight = MainCanvas.ActualHeight > 0 ? MainCanvas.ActualHeight : 1080;
newY = Math.Max(PointRadiusY, Math.Min(canvasHeight - PointRadiusY, newY));
double canvasWidth = MainCanvas.ActualWidth > 0 ? MainCanvas.ActualWidth : 1920;
newX = Math.Max(PointRadiusX, Math.Min(canvasWidth - PointRadiusX, newX));
// 更新点位数据
point.Y = newY;
point.X = newX;
// 更新椭圆位置
Canvas.SetTop(_draggedEllipse, newY - PointRadiusY);
Canvas.SetLeft(_draggedEllipse, newX - PointRadiusX);
// 更新光晕位置
UpdateGlowPosition(point);
// 更新标签位置
UpdateLabelPosition(point);
// 更新引导线
UpdateGuideLines(point, canvasWidth, canvasHeight);
// 更新连接线
UpdateConnectionLine();
// 更新坐标显示
UpdateCoordinateDisplay(new Point(point.X, newY), $"P{point.Id}");
// 更新右侧数据列表
RefreshPointsDataList();
// 重置拖动起始位置
_dragStartX = currentPos.X;
_dragStartY = currentPos.Y;
_originalX = newX;
_originalY = newY;
}
}
}
/// <summary>
/// 点位鼠标释放事件(在椭圆上直接处理)
/// </summary>
private void Point_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isDragging)
{
_isDragging = false;
if (_draggedEllipse != null)
{
_draggedEllipse.ReleaseMouseCapture();
Canvas.SetZIndex(_draggedEllipse, 10);
// 恢复颜色
_draggedEllipse.Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#34D399"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#059669"), 1)
}
};
_draggedEllipse.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#059669"));
if (_draggedEllipse.Tag is JumpRopePoint point)
{
UpdateStatus($"P{point.Id} 点位置已调整为 Y = {Math.Round(point.Y)}");
}
_draggedEllipse = null;
}
// 隐藏坐标显示
CoordinateDisplay.Visibility = Visibility.Collapsed;
}
}
/// <summary>
/// 更新光晕位置
/// </summary>
private void UpdateGlowPosition(JumpRopePoint point)
{
// 找到对应的椭圆
var ellipse = MainCanvas.Children
.OfType<Ellipse>()
.FirstOrDefault(e => e.Name == $"PointEllipse_{point.Id}");
if (ellipse != null)
{
// 光晕宽度
double glowWidth = (PointRadiusX + 10) * 2;
double glowHeight = (PointRadiusY + 10) * 2;
double expectedLeft = point.X - glowWidth / 2;
// 查找光晕(根据尺寸和位置判断)
var glow = MainCanvas.Children
.OfType<Ellipse>()
.FirstOrDefault(e =>
{
var left = Canvas.GetLeft(e);
var top = Canvas.GetTop(e);
return e.Width == glowWidth &&
e.Height == glowHeight &&
Math.Abs(left - expectedLeft) < 10;
});
if (glow != null)
{
Canvas.SetLeft(glow, point.X - glowWidth / 2);
Canvas.SetTop(glow, point.Y - glowHeight / 2);
}
}
}
/// <summary>
/// 更新标签位置
/// </summary>
private void UpdateLabelPosition(JumpRopePoint point)
{
var labelBorder = MainCanvas.Children.OfType<Border>().FirstOrDefault(e => e.Name == $"label{point.Id}");
if (labelBorder != null)
{
Canvas.SetLeft(labelBorder, point.X - PointRadiusX);
Canvas.SetTop(labelBorder, point.Y - PointRadiusY);
}
}
/// <summary>
/// 更新引导线
/// </summary>
private void UpdateGuideLines(JumpRopePoint point, double canvasWidth, double canvasHeight)
{
// 更新 X 轴引导线(垂直线)
var xGuideLine = MainCanvas.Children
.OfType<Line>()
.FirstOrDefault(l => l.Name == $"XGuideLine_{point.Id}");
if (xGuideLine != null)
{
xGuideLine.X1 = point.X;
xGuideLine.X2 = point.X;
xGuideLine.Y1 = 0;
xGuideLine.Y2 = canvasHeight;
}
// 更新 Y 轴引导线(水平线)
var yGuideLine = MainCanvas.Children
.OfType<Line>()
.FirstOrDefault(l => l.Name == $"YGuideLine_{point.Id}");
if (yGuideLine != null)
{
yGuideLine.X1 = 0;
yGuideLine.X2 = canvasWidth;
yGuideLine.Y1 = point.Y;
yGuideLine.Y2 = point.Y;
}
}
/// <summary>
/// 画布鼠标移动事件
/// </summary>
private void MainCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging && _draggedEllipse != null)
{
var currentY = e.GetPosition(MainCanvas).Y;
var deltaY = currentY - _dragStartY;
if (_draggedEllipse.Tag is JumpRopePoint point)
{
// 计算新位置(只允许 Y 轴移动)
double newY = _originalY + deltaY;
// 限制在画布范围内
double canvasHeight = MainCanvas.ActualHeight > 0 ? MainCanvas.ActualHeight : 800;
newY = Math.Max(PointRadiusY, Math.Min(canvasHeight - PointRadiusY, newY));
// 更新点位数据
point.Y = newY;
// 更新椭圆位置
Canvas.SetTop(_draggedEllipse, newY - PointRadiusY);
// 更新连接线
UpdateConnectionLine();
// 更新坐标显示
UpdateCoordinateDisplay(new Point(point.X, newY), $"P{point.Id}");
// 更新右侧数据列表
RefreshPointsDataList();
// 重置拖动起始位置
_dragStartY = currentY;
_originalY = newY;
}
}
}
/// <summary>
/// 画布鼠标释放事件
/// </summary>
private void MainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isDragging)
{
_isDragging = false;
if (_draggedEllipse != null)
{
_draggedEllipse.ReleaseMouseCapture();
Canvas.SetZIndex(_draggedEllipse, 10);
// 恢复颜色
_draggedEllipse.Fill = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1),
GradientStops = new GradientStopCollection
{
new GradientStop((Color)ColorConverter.ConvertFromString("#34D399"), 0),
new GradientStop((Color)ColorConverter.ConvertFromString("#059669"), 1)
}
};
_draggedEllipse.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#059669"));
if (_draggedEllipse.Tag is JumpRopePoint point)
{
UpdateStatus($"P{point.Id} 点位置已调整为 Y = {Math.Round(point.Y)}");
}
_draggedEllipse = null;
}
// 隐藏坐标显示
CoordinateDisplay.Visibility = Visibility.Collapsed;
}
}
/// <summary>
/// 画布鼠标离开事件
/// </summary>
private void MainCanvas_MouseLeave(object sender, MouseEventArgs e)
{
if (_isDragging)
{
MainCanvas_MouseLeftButtonUp(sender, e);
}
}
private void MainCanvas_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
throw new NotImplementedException();
}
/// <summary>
/// 鼠标进入点位
/// </summary>
private void Point_MouseEnter(object sender, MouseEventArgs e)
{
var ellipse = sender as Ellipse;
ellipse.Opacity = 0.9;
// 显示坐标提示
if (ellipse.Tag is JumpRopePoint point)
{
var position = new Point(point.X, point.Y);
UpdateCoordinateDisplay(position, $"P{point.Id}");
}
}
/// <summary>
/// 鼠标离开点位
/// </summary>
private void Point_MouseLeave(object sender, MouseEventArgs e)
{
var ellipse = sender as Ellipse;
ellipse.Opacity = 1.0;
if (!_isDragging)
{
CoordinateDisplay.Visibility = Visibility.Collapsed;
}
}
/// <summary>
/// 更新坐标显示
/// </summary>
private void UpdateCoordinateDisplay(System.Windows.Point position, string pointName)
{
CoordinateDisplay.Content = $"{pointName}: ({position.X:F0}, {position.Y:F0})";
CoordinateDisplay.Margin = new Thickness(position.X + 15, position.Y - 80, 0, 0);
CoordinateDisplay.Visibility = Visibility.Visible;
}
#endregion
#region 按钮事件处理
/// <summary>
/// 加载点数据按钮点击事件
/// </summary>
private void LoadPointsButton_Click(object sender, RoutedEventArgs e)
{
LoadPointsFromDatabase();
}
/// <summary>
/// 保存点数据按钮点击事件
/// </summary>
private void SavePointsButton_Click(object sender, RoutedEventArgs e)
{
SavePointsToDatabase();
}
/// <summary>
/// 重置点数据按钮点击事件
/// </summary>
private void ResetPointsButton_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show(
"确定要将所有点位重置为默认位置吗?",
"确认重置",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
ResetToDefault();
}
}
/// <summary>
/// 返回首页按钮点击事件
/// </summary>
private void Label_MouseDown(object sender, RoutedEventArgs e)
{
var mainWin = Application.Current.MainWindow as Main;
var newPage = new Home();
mainWin?.SwitchPageWithMaskAnimation(newPage, true);
}
#endregion
#region 保存和重置方法
/// <summary>
/// 保存点位数据到数据库
/// </summary>
private void SavePointsToDatabase()
{
try
{
var app = (App)Application.Current;
using var scope = App.AppHost.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var pointsData = db.GroupPoints.FirstOrDefault(x => x.Code == AppSettings.Code);
if (pointsData == null)
{
pointsData = new GroupPoints { Code = AppSettings.Code };
db.GroupPoints.Add(pointsData);
}
// 获取 Point 类型(支持 System.Drawing.Point 或 System.Windows.Point)
var pointType = Type.GetType("System.Windows.Point, WindowsBase");
if (pointType != null)
{
foreach (var point in _points)
{
var pointProperty = typeof(GroupPoints).GetProperty($"Point{point.Id}");
if (pointProperty != null)
{
// 创建 Point 实例并保存
var pointInstance = Activator.CreateInstance(pointType, point.X, point.Y);
var pointNew = new AiSportsMicrospaceDB.Entities.Point
{
X = point.X,
Y = point.Y
};
pointProperty.SetValue(pointsData, pointNew);
}
}
}
// 保存重要点位索引
var keyPointProperty = typeof(GroupPoints).GetProperty("KeyPointIndex");
var controlPoint = typeof(GroupPoints).GetProperty("ControlPoint");
var controlPointNew = new AiSportsMicrospaceDB.Entities.Point
{
X = _points[_keyPointIndex - 1].X,
Y = _points[_keyPointIndex - 1].Y
};
if (keyPointProperty != null)
{
keyPointProperty.SetValue(pointsData, _keyPointIndex);
controlPoint.SetValue(pointsData, controlPointNew);
}
// 保存音乐跳绳二号位位置到 Point10 字段
if (_musicPoint2Index > 0 && _musicPoint2Index <= _points.Count)
{
var point10Property = typeof(GroupPoints).GetProperty("Point10");
if (point10Property != null)
{
var musicPoint2New = new AiSportsMicrospaceDB.Entities.Point
{
X = _points[_musicPoint2Index - 1].X,
Y = _points[_musicPoint2Index - 1].Y
};
point10Property.SetValue(pointsData, musicPoint2New);
}
}
db.SaveChanges();
UpdateStatus("点数据保存成功");
}
catch (Exception ex)
{
UpdateStatus($"保存点数据失败: {ex.Message}");
}
}
/// <summary>
/// 重置为默认位置
/// </summary>
private void ResetToDefault()
{
try
{
var app = (App)Application.Current;
using var scope = App.AppHost.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// 删除现有的点位数据
var existingData = db.GroupPoints.FirstOrDefault(x => x.Code == AppSettings.Code);
if (existingData != null)
{
db.GroupPoints.Remove(existingData);
db.SaveChanges();
}
// 重新生成默认点位
int pointCount = CalculatePointCount(_homeWidth);
CreateDefaultPoints(pointCount);
UpdateStatus("已重置为默认点位置");
}
catch (Exception ex)
{
UpdateStatus($"重置失败: {ex.Message}");
}
}
/// <summary>
/// 更新状态栏文本
/// </summary>
private void UpdateStatus(string message)
{
StatusText.Text = $"{DateTime.Now:HH:mm:ss} - {message}";
}
#endregion
}
/// <summary>
/// 跳绳点位数据模型
/// </summary>
public class JumpRopePoint
{
/// <summary>
/// 点位编号 (1-7)
/// </summary>
public int Id { get; set; }
/// <summary>
/// 索引
/// </summary>
public int Index { get; set; }
/// <summary>
/// X 坐标(固定不可移动)
/// </summary>
public double X { get; set; }
/// <summary>
/// Y 坐标(可拖动调整)
/// </summary>
public double Y { get; set; }
/// <summary>
/// 背景颜色(用于列表显示)
/// </summary>
public string BackgroundColor { get; set; }
}
}