From fe4a3c9b9b970d2e1d318638a27e59d455e5321e Mon Sep 17 00:00:00 2001 From: Tongg Date: Mon, 16 Dec 2024 13:59:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=91=A8=E4=B8=89=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Language/zh_CN.xaml | 2 +- SparkClient.sln.DotSettings.user | 4 + ViewModel/Grading/GradingResultVM.cs | 20 ++- Views/UserControl/Viewport3D.xaml.cs | 5 +- .../ViewportData/Helper/VideoHelper.cs | 159 ++++++++++++++++++ .../ViewportData/Helper/ViewportHelperPro.cs | 35 +++- .../ViewportData/Helper/ViewportManager.cs | 17 +- .../UserControl/ViewportData/ViewportData.cs | 18 +- 8 files changed, 240 insertions(+), 20 deletions(-) create mode 100644 Views/UserControl/ViewportData/Helper/VideoHelper.cs diff --git a/Language/zh_CN.xaml b/Language/zh_CN.xaml index 74e12bd..34bacec 100644 --- a/Language/zh_CN.xaml +++ b/Language/zh_CN.xaml @@ -105,7 +105,7 @@ 正面视角 截图当前视角到PNG 显示模型面 - 显示模型面 + 显示模型面分类标记 显示模型边框 显示光照 双击选择面 diff --git a/SparkClient.sln.DotSettings.user b/SparkClient.sln.DotSettings.user index e3f03b5..5c02844 100644 --- a/SparkClient.sln.DotSettings.user +++ b/SparkClient.sln.DotSettings.user @@ -1,4 +1,5 @@  + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -6,10 +7,13 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/ViewModel/Grading/GradingResultVM.cs b/ViewModel/Grading/GradingResultVM.cs index d29af69..a868392 100644 --- a/ViewModel/Grading/GradingResultVM.cs +++ b/ViewModel/Grading/GradingResultVM.cs @@ -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 diff --git a/Views/UserControl/Viewport3D.xaml.cs b/Views/UserControl/Viewport3D.xaml.cs index b164436..c003abc 100644 --- a/Views/UserControl/Viewport3D.xaml.cs +++ b/Views/UserControl/Viewport3D.xaml.cs @@ -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": diff --git a/Views/UserControl/ViewportData/Helper/VideoHelper.cs b/Views/UserControl/ViewportData/Helper/VideoHelper.cs new file mode 100644 index 0000000..6e744f8 --- /dev/null +++ b/Views/UserControl/ViewportData/Helper/VideoHelper.cs @@ -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 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> 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 GenPng(Viewport3DX viewport) + { + List result = new List(); + + 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; + } +} \ No newline at end of file diff --git a/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs b/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs index ba11257..aa31041 100644 --- a/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs +++ b/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs @@ -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 pngList = await generationTask; + + VideoHelper.CreateVideoFromPngList(pngList, filePath); + } + /// /// 导出模型 /// diff --git a/Views/UserControl/ViewportData/Helper/ViewportManager.cs b/Views/UserControl/ViewportData/Helper/ViewportManager.cs index 6e2b91a..a376140 100644 --- a/Views/UserControl/ViewportData/Helper/ViewportManager.cs +++ b/Views/UserControl/ViewportData/Helper/ViewportManager.cs @@ -46,6 +46,8 @@ public class ViewportManager /// 模型光照 /// public static List MainModelLighting = new List(); + + public static List MainModelMeshes = new List(); /// /// 模型控件对象映射 /// @@ -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); + }); } } diff --git a/Views/UserControl/ViewportData/ViewportData.cs b/Views/UserControl/ViewportData/ViewportData.cs index 54a3448..5c1fa6e 100644 --- a/Views/UserControl/ViewportData/ViewportData.cs +++ b/Views/UserControl/ViewportData/ViewportData.cs @@ -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