sunhonglei 4 months ago
commit caac05598b
  1. 4
      App.config
  2. 3
      App.xaml
  3. 3
      Language/zh_CN.xaml
  4. 11
      Model/Helper/ConfigurationHelper.cs
  5. 8
      ViewModel/BaseWindow/BaseControlVM.cs
  6. 25
      ViewModel/Grading/DiamondSelectVM.cs
  7. 300
      ViewModel/Grading/GradingLoadingVM.cs
  8. 2
      Views/BaseWindow/BaseControl.xaml
  9. 145
      Views/Grading/GradingLoading.xaml
  10. 277
      Views/Grading/GradingLoading.xaml.cs
  11. 35
      Views/UserControl/ViewportData/Helper/ObjExporter.cs

@ -2,8 +2,8 @@
<configuration>
<appSettings>
<!-- 切工仪URL -->
<!-- <add key="BaseUrl" value="http://192.168.10.185:5000/api/SoC"/> -->
<add key="BaseUrl" value="http://192.168.3.100:8080"/>
<add key="BaseUrl" value="http://192.168.10.111:5000/api/SoC"/>
<!-- <add key="BaseUrl" value="http://192.168.3.100:8080"/> -->
<!-- SOC下载图片目录 -->
<add key="ImageFileBasePath" value="D:\diamond_images"/>
<!-- 算法失败时,历史图片保存文件夹路径 -->

@ -74,6 +74,9 @@
<DataTemplate DataType="{x:Type gradingVM:GradingResultVM}">
<gradingView:GradingResult/>
</DataTemplate>
<DataTemplate DataType="{x:Type gradingVM:GradingLoadingVM}">
<gradingView:GradingLoading/>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>

@ -228,4 +228,7 @@
<sys:String x:Key="NotNvidia">未找到Nvidia显卡驱动,部分功能可能无法正常运行。</sys:String>
<sys:String x:Key="NotNvidiaVersionLow">Nvidia显卡驱动版本过旧,请尝试升级显卡驱动。(当前:%version,最低:528.33)</sys:String>
<sys:String x:Key="GradingLoading">正在检测,请稍等</sys:String>
<sys:String x:Key="GradingLoadingStop">终止检测</sys:String>
</ResourceDictionary>

@ -13,9 +13,16 @@ public class ConfigurationHelper
public static string ReadConfigValue(string key)
{
string name = config.AppSettings.Settings[key].Value;
try
{
string name = config.AppSettings.Settings[key].Value;
return name;
return name;
}
catch (Exception e)
{
return "";
}
}
/// <summary>
/// 写入配置

@ -18,6 +18,7 @@ public class BaseControlVM : BaseViewModel
public string WindowTitle { get { return _windowTitle; } set { _windowTitle = value; OnPropertyChanged("WindowTitle"); } }
public Visibility ShowFunctionButton { get { return _showFunctionButton; } set { _showFunctionButton = value; OnPropertyChanged("ShowFunctionButton"); } }
public Visibility ShowCloseButton { get { return _showFunctionButton; } set { _showFunctionButton = value; OnPropertyChanged("ShowFunctionButton"); } }
public string FunctionButtonIcon { get { return _functionButtonIcon; } set { _functionButtonIcon = value; OnPropertyChanged("FunctionButtonIcon"); } }
public Object ViewControl { get { return _viewContent; } set { _viewContent = value; OnPropertyChanged("ViewControl"); } }
@ -55,6 +56,7 @@ public class BaseControlVM : BaseViewModel
Content = vm;
WindowTitle = windowTitle;
ShowFunctionButton = Visibility.Hidden;
ShowCloseButton = Visibility.Visible;
CloseCommand = new RelayCommand(CloseVM);
NextCommand = new RelayCommand(NextDiamond);
@ -80,11 +82,9 @@ public class BaseControlVM : BaseViewModel
}
});
}
else
else if(vm.GetType().Equals(typeof(GradingLoadingVM)))
{
// _hotKeyManager = new HotKeyManager();
// var hotKey = new HotKey(Key.R, ModifierKeys.Control | ModifierKeys.Alt);
// _hotKeyManager.Unregister(hotKey);
ShowCloseButton = Visibility.Hidden;
}
}

@ -144,9 +144,30 @@ public class DiamondSelectVM : BaseViewModel
{
DiamondCode = "";
}
var loadingView = new GradingLoadingVM(param.ToString(), DiamondCode);
BaseControlVM vm = new BaseControlVM(loadingView, MultilingualHelper.getString("DetectionResult"));
vm.WindowTitle = string.IsNullOrWhiteSpace(DiamondCode) ? vm.WindowTitle : $"{vm.WindowTitle} - {DiamondCode}";
WindowManager.mainViewModel.Content = vm;
WindowManager.openContent.Add(vm);
int res = await loadingView.Start();
if (res < 0)
{
//返回
loadingView.Dispose();
WindowManager.mainViewModel.Content = WindowManager.PreviousVM();
;
return;
}
else
{
//获取结果,下一步
}
#if DEBUG
DoStartGrading(param);
#else
//DoStartGrading(param);
//#else
LoadingDialog loading = new LoadingDialog(DiamondCode);
try
{

@ -0,0 +1,300 @@
using System.IO;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SparkClient.Model.Common;
using SparkClient.Model.Helper;
using SparkClient.Model.Services;
using SparkClient.Views.Dialog;
namespace SparkClient.ViewModel.Grading;
public class GradingLoadingVM : BaseViewModel,IDisposable
{
private double _progress;
private SOCClientService _socClientService;
/// <summary>
/// 进度
/// </summary>
public double Progress
{
get => _progress;
set
{
_progress = value;
OnPropertyChanged(nameof(Progress));
}
}
public ICommand StopCommand { get; }
private string _diamondCode;
private string _diamnondType;
private bool _disposed;
private CancellationTokenSource _progressCts;
private CancellationTokenSource? _playbackCts;
#region 图片播放控制
private PlayStatus _currentStatus = PlayStatus.Stopped;
public PlayStatus CurrentStatus
{
get => _currentStatus;
set
{
_currentStatus = value;
OnPropertyChanged(nameof(CurrentStatus));
OnPropertyChanged(nameof(ButtonText)); // 状态变更时更新按钮文本
}
}
public string ButtonText => CurrentStatus switch
{
PlayStatus.Playing => "暂停",
PlayStatus.Paused => "继续",
_ => "重播" // Stopped 状态
};
private CancellationTokenSource _cts;
private int _playDelay = 200; // 默认播放速度
public ICommand PlayControlCommand { get; }
public ICommand PreviousCommand { get; }
public ICommand NextCommand { get; }
private int _currentIndex;
public int CurrentIndex
{
get => _currentIndex;
set
{
_currentIndex = value;
OnPropertyChanged(nameof(CurrentIndex));
UpdateCurrentImage();
}
}
public string[] ImagePaths { get; set; }
public ImageSource CurrentImage { get; private set; }
public bool ImageIsEnable { get; private set; }
#endregion
public GradingLoadingVM(string diamnondType, string diamondCode)
{
_diamondCode = diamondCode;
_diamnondType = diamnondType;
StopCommand = new RelayCommand(Stop);
PlayControlCommand = new RelayCommand(async _ => await HandlePlayControl());
PreviousCommand = new RelayCommand(_ => MovePrevious());
NextCommand = new RelayCommand(_ => MoveNext());
ImageIsEnable = false;
_progressCts = new CancellationTokenSource();
_playbackCts = new CancellationTokenSource();
}
/// <summary>
/// 开始检测
/// </summary>
public async Task<int> Start()
{
//开始显示进度条
//切工仪通讯 拉取图片 延时...5秒
//播放图片
//图片拉取完毕---|
// var configValue = ConfigurationHelper.ReadConfigValue("ProgressTime");
// int iProgTime = int.TryParse(configValue, out var result) ? result : 50000;
// int stepTime = iProgTime / 97;
//
// for (int i = 0; i <= 97; i++)
// {
// _progressCts.Token.ThrowIfCancellationRequested();
// Progress = i;
// await Task.Delay(stepTime, _progressCts.Token);
// }
var progress = RunProgressAsync(_progressCts.Token);
_socClientService = new SOCClientService();
var processImage = _socClientService.ProcessImageCollectionAsync();
//通知页面可以播放图片
await processImage;
if (!"ok".Equals(processImage.Result.Status))
{
_progressCts.Cancel();
new MessageBox().Show(MultilingualHelper.getString(StatusCodes.GetConstantNameByValue(processImage.Result.Status)));
return -1;
}
LoadImages(processImage.Result.Images);
ImageIsEnable = true;
await progress;
return 0;
}
private void Stop(object param)
{
//询问?停止:忽略
}
private async Task RunProgressAsync(CancellationToken token)
{
var configValue = ConfigurationHelper.ReadConfigValue("ProgressTime");
int totalDuration = int.TryParse(configValue, out var result) ? result : 50000;
int stepTime = totalDuration / 97;
// 使用 IProgress 实现线程安全的进度报告
var progress = new Progress<double>(value =>
{
if (!token.IsCancellationRequested)
Progress = value;
});
await Task.Run(async () =>
{
for (int i = 0; i <= 97; i++)
{
token.ThrowIfCancellationRequested();
// 报告进度
((IProgress<double>)progress).Report(i);
// 使用可取消的延迟
await Task.Delay(stepTime, token);
}
}, token);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 取消所有操作
_progressCts?.Cancel();
_playbackCts?.Cancel();
// 释放托管资源
_progressCts?.Dispose();
_playbackCts?.Dispose();
}
_disposed = true;
}
#region 图片播放处理
public void LoadImages(string folderPath)
{
ImagePaths = Directory.GetFiles(folderPath, "*.bmp");
CurrentIndex = 0;
CurrentStatus = PlayStatus.Stopped;
}
public void LoadImages(List<string> folderPath)
{
ImagePaths = folderPath.ToArray();
CurrentIndex = 0;
CurrentStatus = PlayStatus.Stopped;
}
private async Task HandlePlayControl()
{
switch (CurrentStatus)
{
case PlayStatus.Stopped:
await StartPlayback(); // 开始或重播
break;
case PlayStatus.Playing:
PausePlayback(); // 暂停
break;
case PlayStatus.Paused:
await ResumePlayback();// 继续
break;
}
}
private async Task StartPlayback()
{
CurrentStatus = PlayStatus.Playing;
_cts = new CancellationTokenSource();
try
{
for (CurrentIndex = 0; CurrentIndex < ImagePaths.Length; CurrentIndex++)
{
if (_cts.Token.IsCancellationRequested) break;
await Task.Delay(_playDelay);
}
// 播放完成处理
if (CurrentIndex >= ImagePaths.Length)
{
CurrentStatus = PlayStatus.Stopped;
CurrentIndex = 0; // 重置为初始位置
}
}
catch (TaskCanceledException) { /* 正常取消处理 */ }
}
private void PausePlayback()
{
_cts?.Cancel();
CurrentStatus = PlayStatus.Paused;
}
private async Task ResumePlayback()
{
CurrentStatus = PlayStatus.Playing;
_cts = new CancellationTokenSource();
await StartPlayback();
}
private void UpdateCurrentImage()
{
if (ImagePaths == null || CurrentIndex < 0 || CurrentIndex >= ImagePaths.Length)
return;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.UriSource = new Uri(ImagePaths[CurrentIndex]);
bitmap.EndInit();
bitmap.Freeze(); // 确保跨线程安全
CurrentImage = bitmap;
OnPropertyChanged(nameof(CurrentImage));
}
private void MovePrevious()
{
if (CurrentStatus == PlayStatus.Playing)
PausePlayback();
CurrentIndex = (CurrentIndex - 1 + ImagePaths.Length) % ImagePaths.Length;
}
private void MoveNext()
{
if (CurrentStatus == PlayStatus.Playing)
PausePlayback();
CurrentIndex = (CurrentIndex + 1) % ImagePaths.Length;
}
#endregion
}
public enum PlayStatus
{
Stopped, // 初始/停止状态
Playing, // 播放中
Paused // 暂停中
}

@ -157,7 +157,7 @@
Background="Transparent"
Padding="0"
>
<Button Background="Transparent" BorderBrush="Transparent" Padding="0" Command="{Binding CloseCommand}">
<Button Background="Transparent" BorderBrush="Transparent" Padding="0" Command="{Binding CloseCommand}" Visibility="{Binding ShowCloseButton}">
<Image Style="{StaticResource InCloseHoverImageStyle}" x:Name="ImageClosed" Width="25" Height="45"/>
</Button>
</Border>

@ -10,6 +10,7 @@
mc:Ignorable="d"
d:DesignWidth="1920"
d:DesignHeight="600"
Loaded="GradingLoading_OnLoaded"
>
<Border.Resources>
<Style x:Key="BorderRowStyle" TargetType="Border">
@ -137,8 +138,8 @@
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 保存按钮 -->
<Button Grid.Column="0" Padding="0" Margin="10 0 0 0" Height="50" BorderThickness="0" IsEnabled="{Binding IsEnabled}"
Background="Transparent" FocusVisualStyle="{x:Null}" Command="{Binding SaveFileCommand}" ForceCursor="True">
<Button Grid.Column="0" Padding="0" Margin="10 0 0 0" Height="50" BorderThickness="0" IsEnabled="False"
Background="Transparent" FocusVisualStyle="{x:Null}" ForceCursor="True">
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
@ -158,8 +159,8 @@
</Border>
</Button>
<!-- 导出按钮 -->
<Button Grid.Column="1" Padding="0" Margin="10 0" Height="50" BorderThickness="0" IsEnabled="{Binding IsEnabled}"
Background="Transparent" FocusVisualStyle="{x:Null}" Command="{Binding SaveAsCommand}" >
<Button Grid.Column="1" Padding="0" Margin="10 0" Height="50" BorderThickness="0" IsEnabled="False"
Background="Transparent" FocusVisualStyle="{x:Null}" >
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
@ -178,24 +179,10 @@
</Border>
</Button>
<!-- DS 下拉列表 -->
<Border Grid.Column="3" Background="#E0E2EE" Width="170" Height="40" CornerRadius="10" Margin="2,20,30,20" >
<Grid Margin="-2,0,2,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- <TextBlock Grid.Column="1" Text="{StaticResource DiamondResultDs}" TextAlignment="Center" VerticalAlignment="Center" -->
<!-- FontSize="16"/> -->
<!-- <ComboBox Grid.Column="2" ItemsSource="{Binding DSList}" DisplayMemberPath="Key" SelectedValuePath="Value" -->
<!-- SelectedValue="{Binding DS, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" -->
<!-- Margin="10 0 0 0" FontSize="16" Width="80" BorderBrush="Black" BorderThickness="0" Height="30"/> -->
</Grid>
</Border>
<Border Grid.Column="3" Background="#E0E2EE" Width="170" Height="40" CornerRadius="10" Margin="2,20,30,20" />
<!-- 打印按钮 -->
<Button Grid.Column="2" Padding="0" Margin="0,0,10,0" Height="50" BorderThickness="0" IsEnabled="{Binding IsEnabled}"
Background="Transparent" FocusVisualStyle="{x:Null}" Command="{Binding ThermalPrintCommand}" HorizontalAlignment="Left">
<Button Grid.Column="2" Padding="0" Margin="0,0,10,0" Height="50" BorderThickness="0" IsEnabled="False"
Background="Transparent" FocusVisualStyle="{x:Null}" HorizontalAlignment="Left">
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
@ -328,30 +315,140 @@
</Border>
<Border Grid.Row="2" Grid.Column="0" Style="{StaticResource BorderRowStyle}" >
<Image x:Name="VideoImage" />
</Border>
<GridSplitter Grid.Row="2" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" Background="Transparent" IsEnabled="False"></GridSplitter>
<Border Grid.Row="2" Grid.Column="2" Background="White" Style="{StaticResource BorderRowStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="检测中,请稍等..." BorderBrush="Transparent" BorderThickness="0"
TextAlignment="Center" FontSize="26"/>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{StaticResource GradingLoading}" BorderBrush="Transparent" BorderThickness="0"
TextAlignment="Center" FontSize="30"/>
<ProgressBar Grid.Row="1" Margin="50 0" Height="30" Value="{Binding Progress}" FontSize="24"/>
</Grid>
<Grid Grid.Row="1" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="1" Padding="0" Margin="0" Height="80" BorderThickness="0"
Background="Transparent" FocusVisualStyle="{x:Null}" ForceCursor="True">
<Border
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0 0 0 0"
Padding="10, 10"
Width="300"
BorderThickness="0"
CornerRadius="20">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#ee0078" Offset="0"/>
<GradientStop Color="#d8007f" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock FontSize="18" TextAlignment="Center" Foreground="#ffffff" IsEnabled="False" Text="{StaticResource GradingLoadingStop}" />
</Border>
</Button>
<Border Grid.Row="2" Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0 0 0 0"
Padding="10, 10"
Width="310"
Height="80"
BorderThickness="0"
CornerRadius="20">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Padding="0" Margin="00 0 0 0" Height="80" BorderThickness="0" IsEnabled="{Binding ImageIsEnable}"
Background="Transparent" FocusVisualStyle="{x:Null}" Command="{Binding PreviousCommand}" ForceCursor="True">
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0"
Padding="10, 10"
Width="90"
BorderThickness="0"
CornerRadius="20 0 0 20">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#0b9fda" Offset="0"/>
<GradientStop Color="#107fd0" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock FontSize="18" TextAlignment="Center" Foreground="#ffffff" IsEnabled="False" Text="上一帧" />
</Border>
</Button>
<Button Grid.Column="1" Padding="0" Margin="10 0 10 0" Height="80" BorderThickness="0" IsEnabled="{Binding ImageIsEnable}"
Background="Transparent" FocusVisualStyle="{x:Null}" Command="{Binding PlayControlCommand}" ForceCursor="True">
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0"
Padding="10, 10"
Width="90"
BorderThickness="0"
CornerRadius="00 0 0 00">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#0b9fda" Offset="0"/>
<GradientStop Color="#107fd0" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock FontSize="18" TextAlignment="Center" Foreground="#ffffff" IsEnabled="False" Text="{Binding ButtonText}" />
</Border>
</Button>
<Button Grid.Column="2" Padding="0" Margin="0" Height="80" BorderThickness="0" IsEnabled="{Binding ImageIsEnable}"
Background="Transparent" FocusVisualStyle="{x:Null}" Command="{Binding NextCommand}" ForceCursor="True">
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0"
Padding="10, 10"
Width="90"
BorderThickness="0"
CornerRadius="00 20 20 00">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#0b9fda" Offset="0"/>
<GradientStop Color="#107fd0" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock FontSize="18" TextAlignment="Center" Foreground="#ffffff" IsEnabled="False" Text="下一帧" />
</Border>
</Button>
</Grid>
</Border>
</Grid>
</Grid>
</Border>

@ -13,285 +13,22 @@ namespace SparkClient.Views.Grading;
public partial class GradingLoading
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(GradingResult));
private static readonly ILog Logger = LogManager.GetLogger(typeof(GradingLoading));
// List<Viewport3DTriangleEntity> triangles = new List<Viewport3DTriangleEntity>();
List<GeometryModel3D> mouseAddModels = new List<GeometryModel3D>();
private GradingLoadingVM _vm;
// 用于记录当前所有已存在的Popup
private List<Popup> _allPopups = new List<Popup>();
public GradingLoading()
{
InitializeComponent();
// 在Window最外层捕获鼠标点击,判断是否点击在Popup之外,如果是则关闭所有Popup
this.PreviewMouseLeftButtonDown += Window_PreviewMouseLeftButtonDown;
//DataContext = new GradingResultVM(null);
// this.Viewport3Dx.EffectsManager = new DefaultEffectsManager();
// this.Viewport3Dx.Camera = new PerspectiveCamera()
// {
// Position = new Point3D(0, 0, 5),
// LookDirection = new Vector3D(0, 0, -1),
// UpDirection = new Vector3D(0, 1, 0),
// FarPlaneDistance = 1000,
// NearPlaneDistance = 0.1
// };
// Loaded += (sender, args) =>
// {
// var entities = Viewport3DHelper.InitDemo(Viewport3Dx);
// triangles.AddRange(entities);
// };
}
/// <summary>
/// 点击模型
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Viewport3Dx_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// // 获取鼠标点击位置
// var mousePosition = e.GetPosition(Viewport3Dx);
//
// // 查找鼠标点击的 3D 对象
// var hits = Viewport3Dx.FindHits(mousePosition);
//
// // 如果没有命中任何 3D 对象
// if (hits == null || hits.Count == 0)
// {
// Console.WriteLine("未点击到任何模型");
// foreach (var item in mouseAddModels)
// {
// Viewport3Dx.Items.Remove(item);
// }
// mouseAddModels.Clear();
// return;
// }
//
// // 获取第一个命中的对象
// var hit = hits[0];
//
// // 检查是否是 MeshGeometryModel3D
// if (hit.ModelHit is MeshGeometryModel3D modelHit)
// {
// foreach (var item in mouseAddModels)
// {
// Viewport3Dx.Items.Remove(item);
// }
// mouseAddModels.Clear();
// // 获取几何信息
// var geometry = modelHit.Geometry as MeshGeometry3D;
// if (geometry != null)
// {
// // 获取命中的三角形索引
// var triangleIndex = hit.TriangleIndices;
//
// // 获取三角形顶点
// var vertex1 = geometry.Positions[triangleIndex.Item1];
// var vertex2 = geometry.Positions[triangleIndex.Item2];
// var vertex3 = geometry.Positions[triangleIndex.Item3];
//
// string strPoint = vertex1.X + ","+ vertex1.Y+","+ vertex1.Z + ";"+ vertex2.X + ","+ vertex2.Y + ","+ vertex2.Z+";"+vertex3.X + ","+ vertex3.Y + ","+ vertex3.Z;
// string triangleCode = Viewport3DTriangleEntity.GenerateMD5Hash(strPoint);
//
// //命中实体
// Viewport3DTriangleEntity res = triangles.Find(e => triangleCode.Equals(e.TriangleCode));
//
// if (res == null)
// return;
//
// //命中面
// List<Viewport3DTriangleEntity> facet = triangles.Where(e => res.PlaneCode.Equals(e.PlaneCode)).ToList();
// var colorFacet = new Color4(1.0f, 1.0f, 0.0f, 1.0f);
// var data1 = Viewport3DHelper.GenerateEmissiveModelByEntity(Viewport3Dx,facet,colorFacet);
// mouseAddModels.AddRange(data1);
// //命中面标线
// var data2 = Viewport3DHelper.GenerateLineTextModelByEntity(Viewport3Dx, facet);
// mouseAddModels.AddRange(data2);
// //命中同类面
// List<Viewport3DTriangleEntity> facetType = triangles.Where(e => res.PlaneType.Equals(e.PlaneType)).ToList();
// //排除自己
// facet.ForEach(e=>facetType.Remove(e));
// var colorFacetType = new Color4(0.9f, 0.9f, 0.7f, 1.0f);
// var data3 = Viewport3DHelper.GenerateEmissiveModelByEntity(Viewport3Dx,facetType,colorFacetType);
// mouseAddModels.AddRange(data3);
// }
// }
// else
// {
// Console.WriteLine("点击的对象不是 MeshGeometryModel3D");
// }
}
private void ChangSymLevel(object sender, SelectionChangedEventArgs e)
{
var d = DataContext as GradingResultVM;
d.ChangeSym(null);
}
// private void dataGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
// {
// detailPopup.IsOpen = false;
// // 获取点击的目标元素
// var element = sender as FrameworkElement;
//
// var cell = FindVisualParent<DataGridCell>(element);
// if (cell == null) return;
//
// var row = FindVisualParent<DataGridRow>(cell);
// if (row == null) return;
//
// if (element != null)
// {
// // 将 firstCell 的边界转换为屏幕坐标系
// var firstCellBounds = cell.PointToScreen(new Point(0, 0));
//
// // 计算Popup的位置
// var popupPosition = new Point(firstCellBounds.X + cell.RenderSize.Width, firstCellBounds.Y + cell.RenderSize.Height);
// var selectObject = row.Item as DataInfo;
// string testItemId = selectObject.TestItemId;
// var d = DataContext as GradingResultVM;
// RowDetail rowDetail = d.getSelectData(testItemId);
// // 设置Popup的位置
// detailPopup.HorizontalOffset = popupPosition.X;
// detailPopup.VerticalOffset = popupPosition.Y;
// detailPopup.IsOpen = true;
// RowDetail.Items.Clear();
// RowDetail.Items.Add(rowDetail);
// }
// }
// 辅助方法:查找父元素
private T FindVisualParent<T>(DependencyObject child) where T : DependencyObject
{
var parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
if (parentObject is T parent)
{
return parent;
}
else
{
return FindVisualParent<T>(parentObject);
}
}
// private void Window_MouseDown(object sender, MouseButtonEventArgs e)
// {
// detailPopup.IsOpen = false;
// }
//
// private void Window_MouseDown(object sender, RoutedEventArgs e)
// {
// detailPopup.IsOpen = false;
// }
private void ClickArea_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var border = sender as Border;
if (border == null) return;
var parentGrid = border.Parent as Grid;
if (parentGrid == null) return;
var popup = parentGrid.FindName("RowPopup") as Popup;
if (popup == null) return;
popup.DataContext = this.DataContext;
// 先关闭所有已显示的 Popup
CloseAllPopups();
var oo = FindVisualParent<DataGridRow>(parentGrid);
if(oo == null) return;
var d = DataContext as GradingResultVM;
RowDetail rowDetail = d.getSelectData((oo.Item as DataInfo).TestItemId);
if (!_allPopups.Contains(popup))
{
_allPopups.Add(popup);
}
// 显示新Popup
popup.IsOpen = true;
popup.Visibility = Visibility.Visible;
}
private void CloseAllPopups()
{
foreach (var p in _allPopups)
{
p.IsOpen = false;
p.Visibility = Visibility.Collapsed;
}
}
private void CloseAllPopup_Click(object sender, RoutedEventArgs e)
{
CloseAllPopups();
}
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// originalSource 是点击处的控件
var clickedElement = e.OriginalSource as DependencyObject;
// 遍历所有已知 Popup
bool isClickOnAnyPopup = false;
foreach (var p in _allPopups)
{
// 如果此 Popup 不可见或未打开,就不用检查了
if (!p.IsOpen)
continue;
// 判断点击处是否在此 Popup 内
if (IsDescendantOf(p.Child, clickedElement))
{
// 找到一个 Popup 包含这个点击
isClickOnAnyPopup = true;
break;
}
}
// 如果没点击到任何 Popup,就关闭全部
if (!isClickOnAnyPopup)
{
CloseAllPopups();
}
}
private bool IsDescendantOf(DependencyObject parent, DependencyObject clicked)
{
if (parent == null || clicked == null)
return false;
// 从 clicked 开始不断向上找 VisualTree
var current = clicked;
while (current != null)
{
if (current == parent)
{
return true;
}
current = VisualTreeHelper.GetParent(current);
}
return false;
}
private void UIElement_OnMouseLeave(object sender, MouseEventArgs e)
{
if (sender is Image image)
if (this.DataContext is GradingLoadingVM vm)
{
image.Source = new BitmapImage(new Uri(@"pack://application:,,,/Resource/Images/UIResource/01-8.png"));
_vm = vm;
}
}
private void UIElement_OnMouseEnter(object sender, MouseEventArgs e)
//加载控制
private void GradingLoading_OnLoaded(object sender, RoutedEventArgs e)
{
if (sender is Image image)
{
image.Source = new BitmapImage(new Uri(@"pack://application:,,,/Resource/Images/UIResource/01-8-1.png"));
}
//
}
}

@ -12,7 +12,6 @@ public class ObjExporter
public static void ExportToObj2(List<Viewport3DTriangleEntity> entities, string outputPath)
{
string obj = GenerateObj(entities);
// Step 2: 写入 OBJ 文件
using (var writer = new StreamWriter(outputPath))
{
writer.Write(obj);
@ -29,7 +28,6 @@ public class ObjExporter
foreach (var group in groups)
{
// Step 1: 组内顶点去重
var localVertices = new Dictionary<Vector3, int>();
var groupVertices = new List<Vector3>();
@ -49,7 +47,6 @@ public class ObjExporter
}
}
// 映射组内索引到全局索引
var vertexMap = new Dictionary<int, int>();
foreach (var v in groupVertices)
{
@ -57,7 +54,7 @@ public class ObjExporter
vertexMap[localVertices[v]] = allVertices.Count; // OBJ索引从1开始
}
// Step 2: 处理法线和面
Vector3 baseNormal = Vector3.Zero;
bool isFirstTriangle = true;
int vnIndex = 0;
@ -76,7 +73,7 @@ public class ObjExporter
int globalI2 = vertexMap[localI2];
int globalI3 = vertexMap[localI3];
// 计算法线
Vector3 edge1 = p2 - p1;
Vector3 edge2 = p3 - p1;
Vector3 normal = Vector3.Cross(edge1, edge2);
@ -90,7 +87,7 @@ public class ObjExporter
vnIndex = allNormals.Count;
}
// 调整法线方向
float dot = Vector3.Dot(normal, baseNormal);
if (dot < 0)
{
@ -101,22 +98,19 @@ public class ObjExporter
}
}
// 构建OBJ内容
StringBuilder objBuilder = new StringBuilder();
// 顶点
foreach (var v in allVertices)
{
objBuilder.AppendLine($"v {v.X:F6} {v.Y:F6} {v.Z:F6}");
}
// 法线
foreach (var vn in allNormals)
{
objBuilder.AppendLine($"vn {vn.X:F6} {vn.Y:F6} {vn.Z:F6}");
}
// 面
foreach (var face in faceLines)
{
objBuilder.AppendLine(face);
@ -131,11 +125,9 @@ public class ObjExporter
private static List<Vector3> ConvexHullSort(List<Vector3> points)
{
if (points.Count <= 3) return points;
// 按 X 坐标排序,若相同则按 Y 排序
var sorted = points.OrderBy(p => p.X).ThenBy(p => p.Y).ToList();
// 构建下凸包和上凸包
var lower = new List<Vector3>();
foreach (var p in sorted)
{
@ -151,8 +143,7 @@ public class ObjExporter
upper.RemoveAt(upper.Count - 1);
upper.Add(p);
}
// 合并凸包(移除重复点)
lower.RemoveAt(lower.Count - 1);
upper.RemoveAt(upper.Count - 1);
return lower.Concat(upper).ToList();
@ -199,9 +190,3 @@ public class ObjExporter
return center / vertices.Count;
}
}
class FaceData
{
public List<Vector3> Vertices { get; set; }
public Vector3 Normal { get; set; }
}
Loading…
Cancel
Save