diff --git a/AiSportsMicrospaceDB/Entities/BasicConfig.cs b/AiSportsMicrospaceDB/Entities/BasicConfig.cs
index 020f307..66caec5 100644
--- a/AiSportsMicrospaceDB/Entities/BasicConfig.cs
+++ b/AiSportsMicrospaceDB/Entities/BasicConfig.cs
@@ -59,5 +59,14 @@ namespace AiSportsMicrospaceDB.Entities
[Comment("端口")]
[Column(TypeName = "nvarchar(20)")]
public string Port { get; set; }
+
+
+ ///
+ ///端口
+ ///
+ [Display(Name = "运动角宽度")]
+ [Comment("运动角宽度")]
+ [Column(TypeName = "int")]
+ public int HomeWidth { get; set; }
}
}
diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml b/Wpf_AiSportsMicrospace/Views/Home.xaml
index 9a09fd5..a240baf 100644
--- a/Wpf_AiSportsMicrospace/Views/Home.xaml
+++ b/Wpf_AiSportsMicrospace/Views/Home.xaml
@@ -110,7 +110,7 @@
-
+
@@ -124,28 +124,36 @@
+
+
+
+
+
-
-
+
+
-
+
-
+
+
+
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs
index 1e0b2b2..b231490 100644
--- a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs
+++ b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs
@@ -89,7 +89,7 @@ namespace Wpf_AiSportsMicrospace
coverFlow.SelectedIndex = 0;
Loaded += Window_Loaded;
Unloaded += Home_Unloaded;
-
+ Load_Base();
Utils.PlayBackgroundMusic("homeprojectselected.mp3", true);
}
@@ -105,7 +105,6 @@ namespace Wpf_AiSportsMicrospace
_mainWin.WebcamClient.StartExtract();
Utils.PreloadGifs();
- Load_Base();
}
private void Home_Unloaded(object sender, RoutedEventArgs e)
{
@@ -291,7 +290,7 @@ namespace Wpf_AiSportsMicrospace
var baseData = await db.BasicConfig.FirstOrDefaultAsync(x => x.Code == AppSettings.Code);
if (baseData == null)
{
- baseData = new BasicConfig { Code = AppSettings.Code, Host = host.Text, UserName = name.Text, Password = password.Text };
+ baseData = new BasicConfig { Code = AppSettings.Code, Host = host.Text, UserName = name.Text, Password = password.Text , HomeWidth = int.Parse(homeWidth.Text)};
db.BasicConfig.Add(baseData);
db.SaveChanges();
}
@@ -300,6 +299,7 @@ namespace Wpf_AiSportsMicrospace
host.Text = baseData.Host;
name.Text = baseData.UserName;
password.Text = baseData.Password;
+ homeWidth.Text = baseData.HomeWidth.ToString();
}
}
@@ -317,7 +317,8 @@ namespace Wpf_AiSportsMicrospace
Code = AppSettings.Code,
Host = host.Text,
UserName = name.Text,
- Password = password.Text
+ Password = password.Text,
+ HomeWidth = int.Parse(homeWidth.Text)
});
}
else
@@ -325,6 +326,7 @@ namespace Wpf_AiSportsMicrospace
baseData.Host = host.Text;
baseData.UserName = name.Text;
baseData.Password = password.Text;
+ baseData.HomeWidth = int.Parse(homeWidth.Text);
db.BasicConfig.Update(baseData);
}
db.SaveChanges();
@@ -336,11 +338,14 @@ namespace Wpf_AiSportsMicrospace
//多人跳绳
private void GoRopeSet(object sender, RoutedEventArgs e)
{
- var app = (App)Application.Current;
- using var scope = App.AppHost.Services.CreateScope();
- var db = scope.ServiceProvider.GetRequiredService();
- var mainWindow = new MainWindow(db);
- mainWindow.Show();
+ //var app = (App)Application.Current;
+ //using var scope = App.AppHost.Services.CreateScope();
+ //var db = scope.ServiceProvider.GetRequiredService();
+ //var mainWindow = new MainWindow(db);
+ //mainWindow.Show();
+
+ var mainWin = Application.Current.MainWindow as Main;
+ mainWin.SwitchPageWithMaskAnimation(new MultiJumpRope(), true);
}
//跳远
diff --git a/Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml b/Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml
new file mode 100644
index 0000000..d96763f
--- /dev/null
+++ b/Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml.cs
new file mode 100644
index 0000000..03f9e7c
--- /dev/null
+++ b/Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml.cs
@@ -0,0 +1,995 @@
+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
+{
+ ///
+ /// 多人大绳点位配置页面
+ /// 功能:
+ /// 1. 根据 HomeWidth 动态生成 5-7 个点位
+ /// 2. 支持 Y 轴拖动调整
+ /// 3. 从数据库加载/保存点位数据
+ ///
+ public partial class MultiJumpRope : UserControl
+ {
+ #region 私有变量
+
+ private bool _isDragging = false;
+ private Ellipse _draggedEllipse;
+ private double _dragStartY;
+ private double _originalY;
+
+ ///
+ /// 点位数据列表
+ ///
+ private List _points = new List();
+
+ ///
+ /// 当前场地宽度
+ ///
+ private double _homeWidth = 6.0;
+
+ ///
+ /// 点位半径(长轴)
+ ///
+ private const double PointRadiusY = 35;
+
+ ///
+ /// 点位短轴(扁平化比例 0.5)
+ ///
+ private const double PointRadiusX = 70;
+
+ private int _lastFrameNumber = -1;
+ private WriteableBitmap _videoBitmap;
+
+ #endregion
+
+ #region 构造函数
+
+ public MultiJumpRope()
+ {
+ InitializeComponent();
+ SetupDragEvents();
+ }
+
+ #endregion
+
+ #region 私有方法
+
+ ///
+ /// 页面加载完成事件
+ ///
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ LoadPointsFromDatabase();
+ }
+
+ ///
+ /// 设置拖拽事件
+ ///
+ private void SetupDragEvents()
+ {
+ MainCanvas.MouseMove += MainCanvas_MouseMove;
+ MainCanvas.MouseLeftButtonUp += MainCanvas_MouseLeftButtonUp;
+ MainCanvas.MouseLeave += MainCanvas_MouseLeave;
+ }
+
+ ///
+ /// 根据 HomeWidth 计算点位数量
+ /// HomeWidth <= 5:5 个点位
+ /// 5 < HomeWidth < 7:6 个点位
+ /// HomeWidth >= 7:7 个点位
+ ///
+ private int CalculatePointCount(double homeWidth)
+ {
+ if (homeWidth <= 5)
+ return 5;
+ else if (homeWidth > 5 && homeWidth < 7)
+ return 6;
+ else
+ return 7;
+ }
+
+ ///
+ /// 从数据库加载点位数据
+ ///
+ private async void LoadPointsFromDatabase()
+ {
+ try
+ {
+ var app = (App)Application.Current;
+ using var scope = App.AppHost.Services.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+
+ // 获取场地宽度配置
+ var baseConfig = await db.BasicConfig.FirstOrDefaultAsync(x => x.Code == AppSettings.Code);
+ if (baseConfig == null)
+ {
+ UpdateStatus("未找到相机主机配置");
+ return;
+ }
+
+ _homeWidth = baseConfig.HomeWidth;
+ int pointCount = CalculatePointCount(_homeWidth);
+
+ // 更新 UI 显示
+ //HomeWidthText.Text = _homeWidth.ToString("F1");
+ PointCountText.Text = $"点位数量: {pointCount} 个";
+
+ // 清空现有控件
+ ClearPoints();
+
+ // 获取已保存的点位数据
+ var pointsData = await db.SixGroupPoints.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();
+ });
+ }
+
+
+ ///
+ /// 清空所有点位控件
+ ///
+ private void ClearPoints()
+ {
+ _points.Clear();
+ PointsContainer.ItemsSource = null;
+ ConnectionLine.Points.Clear();
+
+ // 清除画布上除了背景和固定控件外的所有元素
+ var elementsToRemove = new List();
+ 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);
+ }
+ }
+
+ ///
+ /// 从数据库数据加载点位
+ /// db.SixGroupPoints 包含 Point1_X, Point1_Y, Point2_X, Point2_Y 等字段
+ ///
+ private void LoadPointsFromDatabaseData(SixGroupPoints data, int pointCount)
+ {
+ for (int i = 1; i <= pointCount; i++)
+ {
+ var point = new JumpRopePoint
+ {
+ Id = i,
+ Index = i
+ };
+
+ // 从数据库读取 Point 类型的属性
+ var pointProperty = typeof(SixGroupPoints).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) * 1080;
+ point.Y = (double)(yProp.GetValue(dbPoint) ?? 0) * 1920;
+ }
+ }
+ }
+
+ // 如果坐标为 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);
+ }
+
+ RefreshPointsDataList();
+ UpdateConnectionLine();
+ }
+
+ ///
+ /// 创建默认点位
+ ///
+ 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();
+ }
+
+ ///
+ /// 获取默认 X 坐标(均匀分布)
+ ///
+ 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;
+ }
+
+ ///
+ /// 获取默认 Y 坐标(画布中间)
+ ///
+ private double GetDefaultY()
+ {
+ double canvasHeight = MainCanvas.ActualHeight > 0 ? MainCanvas.ActualHeight : 800;
+ return canvasHeight / 2;
+ }
+
+ ///
+ /// 创建点位控件
+ ///
+ 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 // 垂直方向光标
+ };
+
+ // 绑定数据
+ 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,
+ 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 guideLine = new Line
+ {
+ X1 = point.X,
+ Y1 = 0,
+ X2 = point.X,
+ Y2 = MainCanvas.ActualHeight > 0 ? MainCanvas.ActualHeight : 800,
+ Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#fff")),
+ StrokeThickness = 2,
+ StrokeDashArray = new DoubleCollection { 5, 5 },
+ Opacity = 0.8
+ };
+ Canvas.SetZIndex(guideLine, 2);
+
+ // 添加到画布
+ MainCanvas.Children.Add(glow);
+ MainCanvas.Children.Add(guideLine);
+ MainCanvas.Children.Add(ellipse);
+ MainCanvas.Children.Add(labelBorder);
+ }
+
+ ///
+ /// 更新右侧点位数据列表
+ ///
+ 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;
+ }
+
+ ///
+ /// 更新连接线
+ ///
+ 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 鼠标事件处理
+
+ ///
+ /// 点位鼠标按下事件
+ ///
+ 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;
+ }
+
+ _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 坐标");
+ }
+
+ ///
+ /// 点位鼠标移动事件(在椭圆上直接处理)
+ ///
+ private void Point_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 : 1080;
+ newY = Math.Max(PointRadiusY, Math.Min(canvasHeight - PointRadiusY, newY));
+
+ // 更新点位数据
+ point.Y = newY;
+
+ // 更新椭圆位置
+ Canvas.SetTop(_draggedEllipse, newY - PointRadiusY);
+
+ // 更新光晕位置
+ UpdateGlowPosition(point);
+
+ // 更新标签位置
+ UpdateLabelPosition(point);
+
+ // 更新引导线
+ UpdateGuideLine(point, canvasHeight);
+
+ // 更新连接线
+ UpdateConnectionLine();
+
+ // 更新坐标显示
+ UpdateCoordinateDisplay(new Point(point.X, newY), $"P{point.Id}");
+
+ // 更新右侧数据列表
+ RefreshPointsDataList();
+
+ // 重置拖动起始位置
+ _dragStartY = currentY;
+ _originalY = newY;
+ }
+ }
+ }
+
+ ///
+ /// 点位鼠标释放事件(在椭圆上直接处理)
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 更新光晕位置
+ ///
+ private void UpdateGlowPosition(JumpRopePoint point)
+ {
+ var ellipse = MainCanvas.Children
+ .OfType()
+ .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()
+ .FirstOrDefault(e =>
+ {
+ var left = Canvas.GetLeft(e);
+ return e.Width == glowWidth &&
+ e.Height == glowHeight &&
+ Math.Abs(left - expectedLeft) < 10;
+ });
+
+ if (glow != null)
+ {
+ Canvas.SetTop(glow, point.Y - glow.Height / 2);
+ }
+ }
+ }
+
+ ///
+ /// 更新标签位置
+ ///
+ private void UpdateLabelPosition(JumpRopePoint point)
+ {
+ var ellipse = MainCanvas.Children
+ .OfType()
+ .FirstOrDefault(e => e.Name == $"PointEllipse_{point.Id}");
+
+ if (ellipse != null)
+ {
+ var labelBorder = MainCanvas.Children
+ .OfType()
+ .FirstOrDefault(b =>
+ {
+ var parent = b.Parent as Canvas;
+ if (parent != null)
+ {
+ return Canvas.GetLeft(b) == Canvas.GetLeft(ellipse) &&
+ Canvas.GetTop(b) == Canvas.GetTop(ellipse);
+ }
+ return false;
+ });
+
+ if (labelBorder != null)
+ {
+ Canvas.SetTop(labelBorder, point.Y - PointRadiusY);
+ }
+ }
+ }
+
+ ///
+ /// 更新引导线
+ ///
+ private void UpdateGuideLine(JumpRopePoint point, double canvasHeight)
+ {
+ var guideLine = MainCanvas.Children
+ .OfType()
+ .FirstOrDefault(l => Math.Abs(l.X1 - point.X) < 5);
+
+ if (guideLine != null)
+ {
+ guideLine.Y1 = 0;
+ guideLine.Y2 = canvasHeight;
+ }
+ }
+
+ ///
+ /// 画布鼠标移动事件
+ ///
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// 画布鼠标释放事件
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 画布鼠标离开事件
+ ///
+ 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();
+ }
+
+ ///
+ /// 鼠标进入点位
+ ///
+ 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}");
+ }
+ }
+
+ ///
+ /// 鼠标离开点位
+ ///
+ private void Point_MouseLeave(object sender, MouseEventArgs e)
+ {
+ var ellipse = sender as Ellipse;
+ ellipse.Opacity = 1.0;
+
+ if (!_isDragging)
+ {
+ CoordinateDisplay.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ ///
+ /// 更新坐标显示
+ ///
+ 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 按钮事件处理
+
+ ///
+ /// 加载点数据按钮点击事件
+ ///
+ private void LoadPointsButton_Click(object sender, RoutedEventArgs e)
+ {
+ LoadPointsFromDatabase();
+ }
+
+ ///
+ /// 保存点数据按钮点击事件
+ ///
+ private void SavePointsButton_Click(object sender, RoutedEventArgs e)
+ {
+ SavePointsToDatabase();
+ }
+
+ ///
+ /// 重置点数据按钮点击事件
+ ///
+ private void ResetPointsButton_Click(object sender, RoutedEventArgs e)
+ {
+ var result = MessageBox.Show(
+ "确定要将所有点位重置为默认位置吗?",
+ "确认重置",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Question);
+
+ if (result == MessageBoxResult.Yes)
+ {
+ ResetToDefault();
+ }
+ }
+
+ ///
+ /// 返回首页按钮点击事件
+ ///
+ 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 保存和重置方法
+
+ ///
+ /// 保存点位数据到数据库
+ ///
+ private void SavePointsToDatabase()
+ {
+ try
+ {
+ var app = (App)Application.Current;
+ using var scope = App.AppHost.Services.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+
+ var pointsData = db.SixGroupPoints.FirstOrDefault(x => x.Code == AppSettings.Code);
+
+ if (pointsData == null)
+ {
+ pointsData = new SixGroupPoints { Code = AppSettings.Code };
+ db.SixGroupPoints.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(SixGroupPoints).GetProperty($"Point{point.Id}");
+
+ if (pointProperty != null)
+ {
+ // 创建 Point 实例并保存
+ var pointInstance = Activator.CreateInstance(pointType, point.X / 1080, point.Y / 1920);
+ var pointNew = new AiSportsMicrospaceDB.Entities.Point {
+ X =point.X / 1080,Y= point.Y / 1920
+ };
+ pointProperty.SetValue(pointsData, pointNew);
+ }
+ }
+ }
+ db.SaveChanges();
+ UpdateStatus("点数据保存成功");
+ }
+ catch (Exception ex)
+ {
+ UpdateStatus($"保存点数据失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 重置为默认位置
+ ///
+ private void ResetToDefault()
+ {
+ try
+ {
+ var app = (App)Application.Current;
+ using var scope = App.AppHost.Services.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+
+ // 删除现有的点位数据
+ var existingData = db.SixGroupPoints.FirstOrDefault(x => x.Code == AppSettings.Code);
+ if (existingData != null)
+ {
+ db.SixGroupPoints.Remove(existingData);
+ db.SaveChanges();
+ }
+
+ // 重新生成默认点位
+ int pointCount = CalculatePointCount(_homeWidth);
+ CreateDefaultPoints(pointCount);
+
+ UpdateStatus("已重置为默认点位置");
+ }
+ catch (Exception ex)
+ {
+ UpdateStatus($"重置失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 更新状态栏文本
+ ///
+ private void UpdateStatus(string message)
+ {
+ StatusText.Text = $"{DateTime.Now:HH:mm:ss} - {message}";
+ }
+
+ #endregion
+ }
+
+ ///
+ /// 跳绳点位数据模型
+ ///
+ public class JumpRopePoint
+ {
+ ///
+ /// 点位编号 (1-7)
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 索引
+ ///
+ public int Index { get; set; }
+
+ ///
+ /// X 坐标(固定不可移动)
+ ///
+ public double X { get; set; }
+
+ ///
+ /// Y 坐标(可拖动调整)
+ ///
+ public double Y { get; set; }
+
+ ///
+ /// 背景颜色(用于列表显示)
+ ///
+ public string BackgroundColor { get; set; }
+ }
+}