update:多人跳绳点位配置
This commit is contained in:
parent
3e8df48443
commit
ad713cfe8f
@ -59,5 +59,14 @@ namespace AiSportsMicrospaceDB.Entities
|
||||
[Comment("端口")]
|
||||
[Column(TypeName = "nvarchar(20)")]
|
||||
public string Port { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
///端口
|
||||
/// </summary>
|
||||
[Display(Name = "运动角宽度")]
|
||||
[Comment("运动角宽度")]
|
||||
[Column(TypeName = "int")]
|
||||
public int HomeWidth { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
|
||||
<!-- 密码弹窗 -->
|
||||
<Grid x:Name="SetDialogGrid" Visibility="Hidden" Background="#80000000">
|
||||
<Border CornerRadius="10" Width="420" Height="330" Background="#FFF">
|
||||
<Border CornerRadius="10" Width="420" Height="530" Background="#FFF">
|
||||
<Grid HorizontalAlignment="Center" hc:BorderElement.CornerRadius="10" VerticalAlignment="Top" Width="420" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@ -124,28 +124,36 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="基础配置" FontSize="18" Margin="12" FontWeight="Bold" Grid.Row="0"/>
|
||||
<TextBlock Text="*重新配置摄像头后请ping测试通过再保存" FontSize="10" Margin="30,34,10,5" Foreground="red" Grid.RowSpan="1"/>
|
||||
<TextBlock Text="摄像头配置" FontSize="18" Margin="12,20,0,0" FontWeight="Bold" Grid.Row="0"/>
|
||||
<TextBlock Text="*重新配置摄像头后请ping测试通过再保存" FontSize="10" Margin="30,54,10,5" Foreground="red" Grid.RowSpan="1"/>
|
||||
<hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="摄像头IP:" Width="400" Text="172.17.30.64" Grid.Row="2" x:Name="host" />
|
||||
<hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="账号:" Width="400" Text="admin" Grid.Row="3" Margin="0,15" x:Name="name"/>
|
||||
<hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="密码:" Width="400" Text="yd708090" Grid.Row="4" x:Name="password" />
|
||||
<StackPanel Grid.Row="7" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12">
|
||||
<StackPanel Grid.Row="9" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12,20,0,0">
|
||||
<RadioButton Margin="0,0,220,0" Style="{DynamicResource RadioButtonSameAsButtonPrimary}" Content="Ping" Click="Ping_Click" />
|
||||
<RadioButton Style="{DynamicResource RadioGroupItemSingle}" Content="取消" x:Name="SetCancelButton" Click="SetCancelButton_Click" />
|
||||
<RadioButton Margin="10,0" Style="{StaticResource RadioButtonSameAsButtonPrimary}" Content="保存" Foreground="#fff" x:Name="SetOkButton1" Click="SetOkButton_Click"/>
|
||||
<RadioButton Margin="10,0,20,0" Style="{StaticResource RadioButtonSameAsButtonPrimary}" Content="保存" Foreground="#fff" x:Name="SetOkButton1" Click="SetOkButton_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Text="基础配置" FontSize="18" Margin="12,30,0,0" FontWeight="Bold" Grid.Row="7"/>
|
||||
<hc:TextBox hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Title="运动角宽度(米):" Width="400" Text="5" Grid.Row="8" x:Name="homeWidth" />
|
||||
|
||||
|
||||
<TextBlock Text="其他配置" FontSize="18" Margin="12,30,0,0" FontWeight="Bold" Grid.Row="8"/>
|
||||
<StackPanel Grid.Row="9" Orientation="Horizontal" HorizontalAlignment="Center" Margin="12">
|
||||
<TextBlock Text="其他配置" FontSize="18" Margin="12,40,0,0" FontWeight="Bold" Grid.Row="10"/>
|
||||
<StackPanel Grid.Row="11" Orientation="Horizontal" HorizontalAlignment="Center" Margin="12">
|
||||
<RadioButton Style="{DynamicResource RadioButtonSameAsButtonPrimary}" Foreground="#fff" Content="多人跳绳" Click="GoRopeSet" />
|
||||
<RadioButton Margin="30,0,0,0" Style="{StaticResource RadioButtonSameAsButtonPrimary}" Foreground="#fff" Content="立定跳远" Click="GoLongSet"/>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@ -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<AppDbContext>();
|
||||
var mainWindow = new MainWindow(db);
|
||||
mainWindow.Show();
|
||||
//var app = (App)Application.Current;
|
||||
//using var scope = App.AppHost.Services.CreateScope();
|
||||
//var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
//var mainWindow = new MainWindow(db);
|
||||
//mainWindow.Show();
|
||||
|
||||
var mainWin = Application.Current.MainWindow as Main;
|
||||
mainWin.SwitchPageWithMaskAnimation(new MultiJumpRope(), true);
|
||||
}
|
||||
|
||||
//跳远
|
||||
|
||||
170
Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml
Normal file
170
Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml
Normal file
@ -0,0 +1,170 @@
|
||||
<UserControl x:Class="Wpf_AiSportsMicrospace.Views.MultiJumpRope"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
Loaded="Window_Loaded"
|
||||
mc:Ignorable="d" Width="1920" Height="1080">
|
||||
<UserControl.Resources>
|
||||
<DropShadowEffect x:Key="DropShadowEffect"
|
||||
Color="#22000000"
|
||||
BlurRadius="12"
|
||||
ShadowDepth="2"
|
||||
Opacity="0.5"/>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<!-- 视频图像层 -->
|
||||
<Image x:Name="videoImage" Stretch="Uniform" />
|
||||
|
||||
<!-- 主画布层 -->
|
||||
<Canvas x:Name="MainCanvas" >
|
||||
<!-- 背景网格 -->
|
||||
<!--<Canvas.Background>
|
||||
<DrawingBrush Viewport="0,0,50,50" ViewportUnits="Absolute" TileMode="Tile">
|
||||
<DrawingBrush.Drawing>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="White">
|
||||
<GeometryDrawing.Geometry>
|
||||
<RectangleGeometry Rect="0,0,50,50"/>
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing>
|
||||
<GeometryDrawing.Pen>
|
||||
<Pen Brush="LightGray" Thickness="0.5"/>
|
||||
</GeometryDrawing.Pen>
|
||||
<GeometryDrawing.Geometry>
|
||||
<GeometryGroup>
|
||||
<LineGeometry StartPoint="0,0" EndPoint="0,50"/>
|
||||
<LineGeometry StartPoint="0,0" EndPoint="50,0"/>
|
||||
</GeometryGroup>
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
</Canvas.Background>-->
|
||||
|
||||
<!-- 动态生成的点位容器 -->
|
||||
<ItemsControl x:Name="PointsContainer">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Canvas/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- 连接线 -->
|
||||
<Polyline x:Name="ConnectionLine" Stroke="#6366F1"
|
||||
StrokeDashArray="5,5" Opacity="0.6"/>
|
||||
|
||||
<!-- 坐标显示标签 -->
|
||||
<hc:Tag ShowCloseButton="False" IsSelected="True" x:Name="CoordinateDisplay"
|
||||
Content="位置点" Padding="18,0" FontSize="14" Visibility="Visible"/>
|
||||
</Canvas>
|
||||
|
||||
<!-- 返回首页按钮 -->
|
||||
<Button Content="返回首页" Click="Label_MouseDown"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
Margin="20" Width="100" Height="30"/>
|
||||
|
||||
<!-- 控制面板 -->
|
||||
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Top" Margin="20" Width="280" Opacity="0.8">
|
||||
<Border Background="#FFFFFF" Padding="15" CornerRadius="8" Effect="{StaticResource DropShadowEffect}">
|
||||
<StackPanel>
|
||||
<!-- 标题 -->
|
||||
<TextBlock Text="多人跳绳点配置" FontSize="18" FontWeight="Bold"
|
||||
Margin="0,0,0,15" HorizontalAlignment="Center"/>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<Separator Margin="0,0,0,10"/>
|
||||
|
||||
<!-- HomeWidth 显示 -->
|
||||
<!--<TextBlock Text="场地宽度 (HomeWidth):" FontSize="14" Margin="0,5,0,5"/>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,5">
|
||||
<TextBlock x:Name="HomeWidthText" Text="6.0" FontSize="24"
|
||||
FontWeight="Bold" Foreground="#3B82F6"
|
||||
VerticalAlignment="Center" Margin="0,0,15,0"/>
|
||||
<TextBlock Text="米" FontSize="14" VerticalAlignment="Center"
|
||||
Foreground="#6B7280"/>
|
||||
</StackPanel>-->
|
||||
|
||||
<!-- 点位数量显示 -->
|
||||
<Border Background="#DBEAFE" CornerRadius="4" Margin="0,10,0,10">
|
||||
<TextBlock x:Name="PointCountText" Text="点位数量: 6 个"
|
||||
FontSize="16" FontWeight="Bold" Foreground="#1E40AF"
|
||||
HorizontalAlignment="Center" Padding="10,8"/>
|
||||
</Border>
|
||||
|
||||
<!-- 操作说明 -->
|
||||
<TextBlock Text="💡 点击点位后可在 Y 轴方向拖动调整"
|
||||
FontSize="12" Foreground="#6B7280" TextWrapping="Wrap"
|
||||
Margin="0,10,0,10"/>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<Separator Margin="0,5,0,10"/>
|
||||
|
||||
<!-- 功能按钮 -->
|
||||
<Button x:Name="LoadPointsButton" Content="📥 加载点数据"
|
||||
Click="LoadPointsButton_Click"
|
||||
Margin="0,5" Height="35" Background="#10B981" Foreground="White"
|
||||
FontSize="14" FontWeight="Bold"/>
|
||||
|
||||
<Button x:Name="SavePointsButton" Content="💾 保存点数据"
|
||||
Click="SavePointsButton_Click"
|
||||
Margin="0,5" Height="35" Background="#3B82F6" Foreground="White"
|
||||
FontSize="14" FontWeight="Bold"/>
|
||||
|
||||
<Button x:Name="ResetPointsButton" Content="🔄 重置为默认"
|
||||
Click="ResetPointsButton_Click"
|
||||
Margin="0,5" Height="35" Background="#EF4444" Foreground="White"
|
||||
FontSize="14" FontWeight="Bold"/>
|
||||
|
||||
<!-- 状态显示 -->
|
||||
<TextBlock x:Name="StatusText" Text="就绪"
|
||||
Margin="0,15,0,0" TextWrapping="Wrap"
|
||||
FontSize="12" Foreground="#6B7280"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 右侧点位数据面板 -->
|
||||
<Border HorizontalAlignment="Right" VerticalAlignment="Top"
|
||||
Margin="20,20,320,20" Width="220" Background="White" Opacity="0.8"
|
||||
CornerRadius="8" Effect="{StaticResource DropShadowEffect}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="15">
|
||||
<TextBlock Text="点位坐标数据" FontSize="16" FontWeight="Bold"
|
||||
Margin="0,0,0,10" HorizontalAlignment="Center"/>
|
||||
<Separator Margin="0,0,0,10"/>
|
||||
|
||||
<ItemsControl x:Name="PointsDataList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{Binding BackgroundColor}"
|
||||
CornerRadius="4" Margin="0,1">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Margin="10,8" VerticalAlignment="Center">
|
||||
<Ellipse Width="24" Height="24"
|
||||
Fill="#34D399" Margin="0,0,10,0"/>
|
||||
<TextBlock Text="{Binding PointLabel}"
|
||||
Foreground="#000"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Width="24" TextAlignment="Center"/>
|
||||
<TextBlock Text="{Binding CoordinateText}"
|
||||
FontFamily="Consolas"
|
||||
FontSize="13"
|
||||
VerticalAlignment="Center"
|
||||
Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
995
Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml.cs
Normal file
995
Wpf_AiSportsMicrospace/Views/Setting/MultiJumpRope.xaml.cs
Normal file
@ -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
|
||||
{
|
||||
/// <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;
|
||||
|
||||
/// <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;
|
||||
|
||||
#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);
|
||||
|
||||
// 更新 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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// <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.SixGroupPoints 包含 Point1_X, Point1_Y, Point2_X, Point2_Y 等字段
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <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 // 垂直方向光标
|
||||
};
|
||||
|
||||
// 绑定数据
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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 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;
|
||||
}
|
||||
|
||||
_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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
return e.Width == glowWidth &&
|
||||
e.Height == glowHeight &&
|
||||
Math.Abs(left - expectedLeft) < 10;
|
||||
});
|
||||
|
||||
if (glow != null)
|
||||
{
|
||||
Canvas.SetTop(glow, point.Y - glow.Height / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新标签位置
|
||||
/// </summary>
|
||||
private void UpdateLabelPosition(JumpRopePoint point)
|
||||
{
|
||||
var ellipse = MainCanvas.Children
|
||||
.OfType<Ellipse>()
|
||||
.FirstOrDefault(e => e.Name == $"PointEllipse_{point.Id}");
|
||||
|
||||
if (ellipse != null)
|
||||
{
|
||||
var labelBorder = MainCanvas.Children
|
||||
.OfType<Border>()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新引导线
|
||||
/// </summary>
|
||||
private void UpdateGuideLine(JumpRopePoint point, double canvasHeight)
|
||||
{
|
||||
var guideLine = MainCanvas.Children
|
||||
.OfType<Line>()
|
||||
.FirstOrDefault(l => Math.Abs(l.X1 - point.X) < 5);
|
||||
|
||||
if (guideLine != null)
|
||||
{
|
||||
guideLine.Y1 = 0;
|
||||
guideLine.Y2 = canvasHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user