Merge remote-tracking branch 'origin/master'

master
sunhonglei 7 months ago
commit 5eae016fb4
  1. 2
      Language/zh_CN.xaml
  2. 4
      SparkClient.sln.DotSettings.user
  3. 20
      ViewModel/Grading/GradingResultVM.cs
  4. 5
      Views/UserControl/Viewport3D.xaml.cs
  5. 159
      Views/UserControl/ViewportData/Helper/VideoHelper.cs
  6. 35
      Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs
  7. 17
      Views/UserControl/ViewportData/Helper/ViewportManager.cs
  8. 18
      Views/UserControl/ViewportData/ViewportData.cs

@ -105,7 +105,7 @@
<sys:String x:Key="ViewportRightMenuFront">正面视角</sys:String>
<sys:String x:Key="ViewportRightMenuSaveViewToPNG">截图当前视角到PNG</sys:String>
<sys:String x:Key="ViewportRightMenuShowModelFace">显示模型面</sys:String>
<sys:String x:Key="ViewportRightMenuShowModelFaceByType">显示模型面</sys:String>
<sys:String x:Key="ViewportRightMenuShowModelFaceByType">显示模型面分类标记</sys:String>
<sys:String x:Key="ViewportRightMenuShowModelFrame">显示模型边框</sys:String>
<sys:String x:Key="ViewportRightMenuShowLighting">显示光照</sys:String>
<sys:String x:Key="ViewportRightMenuSelectFace">双击选择面</sys:String>

@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAsyncVoidMethodBuilder_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F18_003Feb00c5a4_003FAsyncVoidMethodBuilder_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABaseUriHelper_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbfd90ad81939493b96034353abcd1045825908_003Fdc_003Fff55e936_003FBaseUriHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AButtonBase_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6412d4331611499aab4eb63809a2a83bf60910_003Ffb_003F3185652c_003FButtonBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AButton_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6412d4331611499aab4eb63809a2a83bf60910_003F94_003F46375460_003FButton_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
@ -6,10 +7,13 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClrObjectRuntime_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fab21534b1ec041498b3c23a65e9ed82b15a918_003F19_003F3f4e517b_003FClrObjectRuntime_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AContentControl_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F27bb3fae2c21cbe9a2565477feaa7a5b3a19cd779b4a91a98edfff9cd468972_003FContentControl_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADependencyObject_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F60b63c019ead4a238340b47a1c0010d5226910_003Fca_003F09e9dbc0_003FDependencyObject_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADispatcherOperation_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F60b63c019ead4a238340b47a1c0010d5226910_003Fd8_003F13c94a2a_003FDispatcherOperation_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADispatcher_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F60b63c019ead4a238340b47a1c0010d5226910_003Ff6_003F1c45f799_003FDispatcher_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEventRoute_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F96a561fe76144633acef44f09d0dcb8a825920_003Fb0_003F8f1a0289_003FEventRoute_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEventToCommand_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F436b6c3e323d68842c9e251322f5d42b47569f7c925e63aa245dc65465d2843_003FEventToCommand_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEventTriggerBase_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Ffdc001c928464b80ad45ffa09b838a3a15e200_003Faf_003F9abbeb44_003FEventTriggerBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExecutionContext_002Ecs_002Fl_003AC_0021_003FUsers_003FAdministrator_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F35_003F053c62c1_003FExecutionContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFileSystem_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4c69bf2d25bb4f2497b2dbd14727a7811308b0_003F9b_003F7ca26691_003FFileSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFile_002Ecs_002Fl_003AC_0021_003FUsers_003FAdministrator_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003F00_003Fb0994fb0_003FFile_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFrameworkElement_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6412d4331611499aab4eb63809a2a83bf60910_003F07_003Fdab5922a_003FFrameworkElement_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGuid_002Ecs_002Fl_003AC_0021_003FUsers_003Ftongg_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F211e6f3b24fa438a92f1815153647ce2c8f908_003Faa_003Fa49e75b9_003FGuid_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

@ -571,12 +571,13 @@ public class GradingResultVM : BaseViewModel
}
}
}
private void ExportFile(string filePath)
private async void ExportFile(string filePath)
{
TxtFile(filePath);
ExcelFile(filePath);
DatFile(filePath);
STLFile(filePath);
await DatFile(filePath);
STLFile(filePath);
DataConver(filePath);
}
private void TxtFile(string filePath)
@ -801,13 +802,20 @@ public class GradingResultVM : BaseViewModel
{
return (Math.Floor(value * 10) / 10).ToString(digitsFormat);
}
private void DatFile(string filePath)
private async Task DatFile(string filePath)
{
File.Create(filePath + ".dat").Close();
//File.Create(filePath + ".dat").Close();
await ViewportData.SaveAsToVedioFile(filePath + ".mp4");
}
private void STLFile(string filePath)
{
File.Create(filePath + ".stl").Close();
//File.Create(filePath + ".stl").Close();
ViewportData.SaveAsToStlFile(filePath + ".stl");
}
private void DataConver(string filePath)
{
//File.Create(filePath + ".stl").Close();
ViewportData.ConvertMp4ToDat(filePath + ".mp4",filePath + ".dat");
}
#endregion

@ -258,10 +258,13 @@ public partial class Viewport3D
case "ViewportRightMenuShowModelFace":
//显示模型面
ViewportManager.ShowMainModel3D(checkResult);
// ViewportRightMenuShowModelFace.IsChecked = !checkResult;
// ViewportManager.ShowMainModel3DByType(!checkResult);
break;
case "ViewportRightMenuShowModelFaceByType":
//显示模型面
ViewportManager.ShowMainModel3D(!checkResult);
// ViewportManager.ShowMainModel3D(!checkResult);
// ViewportRightMenuShowModelFaceByType.IsChecked = !checkResult;
ViewportManager.ShowMainModel3DByType(checkResult);
break;
case "ViewportRightMenuShowModelFrame":

@ -0,0 +1,159 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using HelixToolkit.Wpf.SharpDX;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.Windows;
namespace SparkClient.Views.UserControl.ViewportData.Helper;
public class VideoHelper
{
public static async void CreateVideoFromPngList(List<PngBitmapEncoder> pngEncoders, string outputPath)
{
string tempDirectory = Path.Combine(System.Environment.CurrentDirectory, "PngFrames");
Directory.CreateDirectory(tempDirectory);
await Task.Delay(1);
try
{
// Save each PNG frame to a temporary directory
for (int i = 0; i < pngEncoders.Count; i++)
{
string framePath = Path.Combine(tempDirectory, $"frame_{i:D5}.png");
using (FileStream fs = new FileStream(framePath, FileMode.Create))
{
pngEncoders[i].Save(fs);
}
}
// Use FFmpeg to create a video from the PNG frames
string ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe"); // Assuming ffmpeg.exe is in the same directory as the application
if (!File.Exists(ffmpegPath))
{
throw new FileNotFoundException("FFmpeg executable not found.", ffmpegPath);
}
string framePattern = Path.Combine(tempDirectory, "frame_%05d.png").Replace("\\", "/"); // Replace backslashes with forward slashes for FFmpeg compatibility
string arguments = $"-framerate 30 -i \"{framePattern}\" -vf \"scale=640:480\" -c:v libx264 -pix_fmt yuv420p \"{outputPath}\"";
var processInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = System.Diagnostics.Process.Start(processInfo))
{
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (process.ExitCode != 0)
{
throw new Exception($"FFmpeg error: {error}");
}
}
var dicName = Path.GetDirectoryName(outputPath);
var fileName =Path.GetFileNameWithoutExtension(outputPath);
Microsoft.VisualBasic.FileIO.FileSystem.RenameFile(outputPath, fileName+".dat");
}
finally
{
// Clean up temporary frames
Directory.Delete(tempDirectory, true);
}
}
public static void ConvertMp4ToDat(string inputFilePath, string outputFilePath)
{
string ffmpegPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg.exe"); // Assuming ffmpeg.exe is in the same directory as the application
if (!File.Exists(ffmpegPath))
{
throw new FileNotFoundException("FFmpeg executable not found.", ffmpegPath);
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = $"-i \"{outputFilePath}\" \"{inputFilePath}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
File.Delete(outputFilePath);
}
public static async Task<List<PngBitmapEncoder>> StartGenerationAndRotation(Viewport3DX viewport)
{
var genPngTask = Task.Run(() => GenPng(viewport));
var rotateModelTask = Task.Run(() => RotateModel());
await Task.WhenAll(genPngTask, rotateModelTask);
return genPngTask.Result;
}
public static List<PngBitmapEncoder> GenPng(Viewport3DX viewport)
{
List<PngBitmapEncoder> result = new List<PngBitmapEncoder>();
for (int i = 0; i < 10000; i += 50)
{
// 使用 Dispatcher.Invoke 来确保 UI 操作在主线程中执行
var encoder = Application.Current.Dispatcher.Invoke(() => CutPng(viewport));
result.Add(encoder);
Thread.Sleep(50); // 模拟处理过程
}
return result;
}
public static async void RotateModel()
{
await Application.Current.Dispatcher.InvokeAsync(() =>
{
var center = ViewportManager.ModelBounds.Center;
var maxDimension = ViewportManager.ModelBounds.Size.Length();
var distance = maxDimension *1.2; // 调整相机到模型的距离,保证视野范围内
var camera = ViewportManager.GetViewport3D().Camera as HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
camera.Position = new Point3D(center.X, center.Y, center.Z + distance); // 从前面看,Z轴正方向
camera.UpDirection = new Vector3D(0, -1, 0);
camera.LookDirection = new Vector3D(center.X - camera.Position.X, center.Y - camera.Position.Y, center.Z - camera.Position.Z);
ViewportHelperPro.RotateModel(new Vector3D(0,-1,0));
});
await Task.Delay(5000);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
ViewportHelperPro.RotateModel(new Vector3D(-1, 0, 0));
});
}
public static PngBitmapEncoder CutPng(Viewport3DX viewport)
{
int width = (int)viewport.ActualWidth;
int height = (int)viewport.ActualHeight;
var renderTargetBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
renderTargetBitmap.Render(viewport);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
return encoder;
}
}

@ -1,5 +1,6 @@
using System.Collections.Frozen;
using System.Collections.ObjectModel;
using System.Drawing.Text;
using System.IO;
using System.Windows;
using System.Windows.Media;
@ -99,7 +100,7 @@ public class ViewportHelperPro
foreach (var group in groupedDict)
{
var key = group.Key;
result.Add(GenerateModelByEntity(group.Value, GenFaceColor4(key)));
}
return result;
@ -110,10 +111,21 @@ public class ViewportHelperPro
switch (planeType)
{
case PlaneType.Girdle:
return ViewportManager.LightGray;
case PlaneType.Crown :
return new Color4(1, 1, 1, 1);
return new Color4(0, 0.74901f, 1, 0.2f);
case PlaneType.TableFacet :
return new Color4(0.52941f, 0.80734f, 0.98039f, 1);
case PlaneType.UpperMainFacet:
return new Color4(30/255f, 144/255f, 1, 1f);
case PlaneType.StarFacet:
return new Color4(0, 0, 1, 1f);
case PlaneType.UpperGirdleFacet:
return new Color4(100/255f, 149/255f, 237/255f, 1f);
case PlaneType.PavilionMainFacet:
return new Color4(106/255f, 90/255f, 205/255f, 1f);
case PlaneType.LowerGirdleFact:
return new Color4(175/255f, 238/255f, 238/255f, 1f);
case PlaneType.Culet:
return new Color4(255/255f, 255/255f, 255/255f, 1f);
}
return null;
@ -144,6 +156,19 @@ public class ViewportHelperPro
}
}
public static async Task ExportModelsToVideo(Viewport3DX viewport, string filePath)
{
if (viewport == null)
viewport = ViewportManager.GetViewport3D();
var generationTask = VideoHelper.StartGenerationAndRotation(viewport);
List<PngBitmapEncoder> pngList = await generationTask;
VideoHelper.CreateVideoFromPngList(pngList, filePath);
}
/// <summary>
/// 导出模型
/// </summary>

@ -46,6 +46,8 @@ public class ViewportManager
/// 模型光照
/// </summary>
public static List<Light3D> MainModelLighting = new List<Light3D>();
public static List<MeshGeometryModel3D> MainModelMeshes = new List<MeshGeometryModel3D>();
/// <summary>
/// 模型控件对象映射
/// </summary>
@ -159,6 +161,7 @@ public class ViewportManager
MainModel3D = ViewportHelperPro.GenerateModelByEntity(_viewport, entities);
MainModelLines = ViewportHelperPro.GentrateLineByEntity(_viewport, entities);
MainModelLighting = ViewportHelperPro.GenerateLightingForModel(_viewport);
MainModelMeshes = ViewportHelperPro.GenerateModelByEntityGroupByType(entities);
//切换相机视角
_viewport.Camera = ViewportHelperPro.CalculateCamera(PositiveDirection, ModelBounds);
_viewport.Camera.UpDirection = new Vector3D(0, -1, 0);
@ -196,14 +199,18 @@ public class ViewportManager
if (_viewport == null) return;
if (isShow)
{
if(_viewport.Items.Contains(MainModel3D))
return;
else
_viewport.Items.Add(MainModel3D);
MainModelMeshes.ForEach(e =>
{
if(!_viewport.Items.Contains(e))
_viewport.Items.Add(e);
});
}
else
{
_viewport.Items.Remove(MainModel3D);
MainModelMeshes.ForEach(e =>
{
_viewport.Items.Remove(e);
});
}
}

@ -153,11 +153,25 @@ public class ViewportData
}
}
public bool SaveAsToVedioFile(string filename)
public async Task SaveAsToVedioFile(string filename)
{
try
{
ViewportHelperPro.ExportModelsToStl(null, filename);
await ViewportHelperPro.ExportModelsToVideo(null, filename);
}
catch
{
return ;
}
}
public bool ConvertMp4ToDat(string mp4FilePath, string datFilePath)
{
try
{
VideoHelper.ConvertMp4ToDat(datFilePath, mp4FilePath);
return true;
}
catch

Loading…
Cancel
Save