From b47f1e83ae60be97c4196e8de17aa63880643321 Mon Sep 17 00:00:00 2001 From: Tongg Date: Fri, 21 Feb 2025 16:53:56 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=83=A8=E7=BD=B2=E8=B0=83=E6=95=B4?= =?UTF-8?q?=EF=BC=883/3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ViewModel/Grading/DiamondSelectVM.cs | 5 +- .../ViewportData/Helper/ObjExporter.cs | 162 ++++++++++-------- .../ViewportData/Helper/ViewportHelperPro.cs | 2 +- .../ViewportData/Helper/ViewportManager.cs | 8 + 4 files changed, 107 insertions(+), 70 deletions(-) diff --git a/ViewModel/Grading/DiamondSelectVM.cs b/ViewModel/Grading/DiamondSelectVM.cs index 35d28c1..075df85 100644 --- a/ViewModel/Grading/DiamondSelectVM.cs +++ b/ViewModel/Grading/DiamondSelectVM.cs @@ -171,7 +171,7 @@ public class DiamondSelectVM : BaseViewModel } // 模拟耗时操作 //System.Threading.Thread.Sleep(50); // 休眠50毫秒 - await Task.Delay(710); + await Task.Delay(515); loading.setValue(i); progress = i; } @@ -537,7 +537,8 @@ public class DiamondSelectVM : BaseViewModel { //WindowManager.mainViewModel.Content = WindowManager.PreviousVM(); BaseControlVM vm = new BaseControlVM(new GradingResultVM(param), MultilingualHelper.getString("DetectionResult")); - vm.WindowTitle = string.IsNullOrWhiteSpace(param.DiamondCode) ? vm.WindowTitle : $"{vm.WindowTitle} - {param.DiamondCode}"; + var ct = param.measurements.VOLUME / 57; + vm.WindowTitle = string.IsNullOrWhiteSpace(param.DiamondCode) ? vm.WindowTitle : $"{vm.WindowTitle} - {param.DiamondCode}({ct.ToString("F3")}ct)"; vm.ShowFunctionButton = System.Windows.Visibility.Visible; WindowManager.mainViewModel.Content = vm; WindowManager.openContent.Add(vm); diff --git a/Views/UserControl/ViewportData/Helper/ObjExporter.cs b/Views/UserControl/ViewportData/Helper/ObjExporter.cs index 64391e0..eb77f92 100644 --- a/Views/UserControl/ViewportData/Helper/ObjExporter.cs +++ b/Views/UserControl/ViewportData/Helper/ObjExporter.cs @@ -10,92 +10,120 @@ namespace SparkClient.Views.UserControl.ViewportData.Helper; public class ObjExporter { public static void ExportToObj2(List entities, string outputPath) -{ - // 分组:按面代码 PlaneCode 分组 - var faceGroups = entities - .GroupBy(e => e.PlaneCode) - .ToDictionary(g => g.Key, g => g.ToList()); + { + string obj = GenerateObj(entities); + // Step 2: 写入 OBJ 文件 + using (var writer = new StreamWriter(outputPath)) + { + writer.Write(obj); + } + } + + public static string GenerateObj(IEnumerable triangles) + { + var groups = triangles.GroupBy(t => t.PlaneCode); - // 存储每个面的顶点和法线 - var faceData = new Dictionary(); + List allVertices = new List(); + List allNormals = new List(); + List faceLines = new List(); - // Step 1: 处理每个面,生成顶点列表并计算法线 - foreach (var group in faceGroups) - { - // 合并所有顶点并去重 - var vertices = group.Value - .SelectMany(e => new[] { e.Point1, e.Point2, e.Point3 }) - .Distinct() - .ToList(); + foreach (var group in groups) + { + // Step 1: 组内顶点去重 + var localVertices = new Dictionary(); + var groupVertices = new List(); - // 按凸包算法排序顶点(避免交叉) - var sortedVertices = ConvexHullSort(vertices); + foreach (var triangle in group) + { + AddVertex(triangle.Point1); + AddVertex(triangle.Point2); + AddVertex(triangle.Point3); + } - // 计算面的法线(基于排序后的顶点) - var normal = CalculateFaceNormal(sortedVertices, ViewportManager.CenterVector); + void AddVertex(Vector3 v) + { + if (!localVertices.ContainsKey(v)) + { + localVertices[v] = groupVertices.Count; + groupVertices.Add(v); + } + } - // 存储面数据 - faceData[group.Key] = new FaceData - { - Vertices = sortedVertices, - Normal = normal - }; - } + // 映射组内索引到全局索引 + var vertexMap = new Dictionary(); + foreach (var v in groupVertices) + { + allVertices.Add(v); + vertexMap[localVertices[v]] = allVertices.Count; // OBJ索引从1开始 + } - // Step 2: 写入 OBJ 文件 - using (var writer = new StreamWriter(outputPath)) - { - // 写入顶点 - var vertexIndexMap = new Dictionary(); - int index = 1; - foreach (var face in faceData.Values) - { - foreach (var vertex in face.Vertices) - { - if (!vertexIndexMap.ContainsKey(vertex)) + // Step 2: 处理法线和面 + Vector3 baseNormal = Vector3.Zero; + bool isFirstTriangle = true; + int vnIndex = 0; + + foreach (var triangle in group) { - vertexIndexMap[vertex] = index++; - writer.WriteLine($"v {vertex.X} {vertex.Y} {vertex.Z}"); + Vector3 p1 = triangle.Point1; + Vector3 p2 = triangle.Point2; + Vector3 p3 = triangle.Point3; + + int localI1 = localVertices[p1]; + int localI2 = localVertices[p2]; + int localI3 = localVertices[p3]; + + int globalI1 = vertexMap[localI1]; + int globalI2 = vertexMap[localI2]; + int globalI3 = vertexMap[localI3]; + + // 计算法线 + Vector3 edge1 = p2 - p1; + Vector3 edge2 = p3 - p1; + Vector3 normal = Vector3.Cross(edge1, edge2); + normal.Normalize(); + + if (isFirstTriangle) + { + baseNormal = normal; + isFirstTriangle = false; + allNormals.Add(baseNormal); + vnIndex = allNormals.Count; + } + + // 调整法线方向 + float dot = Vector3.Dot(normal, baseNormal); + if (dot < 0) + { + (globalI2, globalI3) = (globalI3, globalI2); + } + + faceLines.Add($"f {globalI1}//{vnIndex} {globalI2}//{vnIndex} {globalI3}//{vnIndex}"); } } - } - // 写入法线(每个面一个法线) - var normalIndexMap = new Dictionary(); - index = 1; - foreach (var face in faceData.Values) - { - if (!normalIndexMap.ContainsKey(face.Normal)) + // 构建OBJ内容 + StringBuilder objBuilder = new StringBuilder(); + + // 顶点 + foreach (var v in allVertices) { - normalIndexMap[face.Normal] = index++; - // writer.WriteLine($"vn {face.Normal.X} {face.Normal.Y} {face.Normal.Z}"); + objBuilder.AppendLine($"v {v.X:F6} {v.Y:F6} {v.Z:F6}"); } - } - - // 写入面 - foreach (var faceEntry in faceData) - { - var face = faceEntry.Value; - var normalIndex = normalIndexMap[face.Normal]; - // 正面(顺时针) - writer.Write("f "); - foreach (var vertex in face.Vertices) + // 法线 + foreach (var vn in allNormals) { - writer.Write($"{vertexIndexMap[vertex]}//{normalIndex} "); + objBuilder.AppendLine($"vn {vn.X:F6} {vn.Y:F6} {vn.Z:F6}"); } - writer.WriteLine(); - // 反面(逆时针) - writer.Write("f "); - foreach (var vertex in face.Vertices.AsEnumerable().Reverse()) + // 面 + foreach (var face in faceLines) { - writer.Write($"{vertexIndexMap[vertex]}//{normalIndex} "); + objBuilder.AppendLine(face); } - writer.WriteLine(); + + return objBuilder.ToString(); } - } -} /// /// 凸包排序(Andrew's Monotone Chain 算法) diff --git a/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs b/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs index 9ff21c3..9234eab 100644 --- a/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs +++ b/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs @@ -375,7 +375,7 @@ public class ViewportHelperPro var facetPoints = VectorClockwiseSort(uniqueVectors.ToList()); Vector3 center = GetCentroid(uniqueVectors.ToList()); - Console.WriteLine($"{yaoFace.Key} \t {center.X} \t {center.Y} \t {center.Z}"); + // Console.WriteLine($"{yaoFace.Key} \t {center.X} \t {center.Y} \t {center.Z}"); List leftLine = new List(); List rightLine = new List(); diff --git a/Views/UserControl/ViewportData/Helper/ViewportManager.cs b/Views/UserControl/ViewportData/Helper/ViewportManager.cs index 3e3e319..4a46201 100644 --- a/Views/UserControl/ViewportData/Helper/ViewportManager.cs +++ b/Views/UserControl/ViewportData/Helper/ViewportManager.cs @@ -361,6 +361,14 @@ public class ViewportManager //切换相机视角 _viewport.Camera = ViewportHelperPro.CalculateCamera(PositiveDirection, ModelBounds); _viewport.Camera.UpDirection = new Vector3D(0, -1, 0); + + var center = ViewportManager.ModelBounds.Center; + var maxDimension = ViewportManager.ModelBounds.Size.Length(); + var distance = maxDimension * 1.2; // 调整相机到模型的距离,保证视野范围内 + var camera = _viewport.Camera as HelixToolkit.Wpf.SharpDX.OrthographicCamera; + camera.Position = new Point3D(center.X, center.Y - distance, center.Z); // 从底部看,Y轴负方向 + camera.UpDirection = new Vector3D(0, 0, -1); + camera.LookDirection = new Vector3D(center.X - camera.Position.X, center.Y - camera.Position.Y, center.Z - camera.Position.Z); _viewport.RenderHost.MSAA = MSAALevel.Maximum; // TODO: InitMeshLines();