using System.IO; using System.Windows.Media; using System.Windows.Media.Media3D; using SharpDX.Direct3D11; using SparkClient.Model.Entity; namespace SparkClient.Model.Helper; using HelixToolkit.Wpf.SharpDX; using SharpDX; using System.Collections.Generic; public class Viewport3DHelper { // 加载环境贴图 // public static Stream stream = // File.Open("D:\\WorkSpace\\spark\\HelixToolkitSharpDX-Demo\\morning_racing_circuit_16k.dds", FileMode.Open); /// /// 生成一个多边形钻石 /// /// 控件 /// 边数 public static void GenerateDiamond(Viewport3DX viewport, int sides) { // 启用法线生成 var meshBuilder = new MeshBuilder(true, false); //设置抗锯齿 viewport.RenderHost.MSAA = MSAALevel.Maximum; double radius = 3.2915; double smallRadius = radius / 2; double topHeight = 1.67; double bottomHeight = 4.43; double smallOffset = 1.25; var largeVertices = new List(); var smallVertices = new List(); var edgeLines = new List>(); // 生成大八边形顶点 for (int i = 0; i < sides; i++) { double angle = i * Math.PI / (sides / 2); largeVertices.Add(new Vector3((float)(radius * Math.Cos(angle)), (float)topHeight, (float)(radius * Math.Sin(angle)))); } // 生成小八边形顶点 for (int i = 0; i < sides; i++) { double angle = i * Math.PI / (sides / 2); smallVertices.Add(new Vector3((float)(smallRadius * Math.Cos(angle)), (float)(topHeight + smallOffset), (float)(smallRadius * Math.Sin(angle)))); } var bottomPoint = new Vector3(0, (float)(topHeight - bottomHeight), 0); var topPoint = new Vector3(0, (float)topHeight, 0); // 生成底部面 for (int i = 0; i < sides; i++) { int nextIndex = (i + 1) % sides; // 计算法线 // Vector3 edge1 = largeVertices[nextIndex] - bottomPoint; // Vector3 edge2 = largeVertices[i] - bottomPoint; // Vector3 normal = Vector3.Cross(edge1, edge2); // normal.Normalize(); List fanPositions2 = new List() { bottomPoint, largeVertices[nextIndex], largeVertices[i] }; List fanNormals2 = new List() { new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0) }; meshBuilder.AddTriangleFan(fanPositions2, fanNormals2); // meshBuilder.AddTriangle(bottomPoint, largeVertices[nextIndex], largeVertices[i]); // meshBuilder.AddTriangle(topPoint, largeVertices[i], largeVertices[nextIndex]); // 添加边框线 edgeLines.Add(new Tuple(bottomPoint, largeVertices[nextIndex])); edgeLines.Add(new Tuple(largeVertices[nextIndex], largeVertices[i])); edgeLines.Add(new Tuple(largeVertices[i], bottomPoint)); } // 生成法线并添加小八边形的面 var smallNormals = new List(); for (int i = 0; i < smallVertices.Count; i++) { smallNormals.Add(new Vector3(0, 1, 0)); // 小八边形法线向上 } meshBuilder.AddTriangleFan(smallVertices, smallNormals); // 生成大八边形和小八边形的连接面 for (int i = 0; i < sides; i++) { int nextIndex = (i + 1) % sides; // 计算法线 // Vector3 edge1 = largeVertices[nextIndex] - largeVertices[i]; // Vector3 edge2 = smallVertices[nextIndex] - largeVertices[i]; // Vector3 normal1 = Vector3.Cross(edge1, edge2); // normal1.Normalize(); // // Vector3 edge3 = smallVertices[nextIndex] - largeVertices[i]; // Vector3 edge4 = smallVertices[i] - largeVertices[i]; // Vector3 normal2 = Vector3.Cross(edge3, edge4); // normal2.Normalize(); List fanPositions1 = new List() { largeVertices[i], largeVertices[nextIndex], smallVertices[nextIndex] }; List fanNormals1 = new List() { new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0) }; meshBuilder.AddTriangleFan(fanPositions1, fanNormals1); //meshBuilder.AddTriangle(largeVertices[i], largeVertices[nextIndex], smallVertices[nextIndex]); List fanPositions2 = new List() { largeVertices[i], smallVertices[nextIndex], smallVertices[i] }; List fanNormals2 = new List() { new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0) }; meshBuilder.AddTriangleFan(fanPositions2, fanNormals2); //meshBuilder.AddTriangle(largeVertices[i], smallVertices[nextIndex], smallVertices[i]); // 添加边框线 edgeLines.Add(new Tuple(largeVertices[i], largeVertices[nextIndex])); edgeLines.Add(new Tuple(largeVertices[nextIndex], smallVertices[nextIndex])); edgeLines.Add(new Tuple(smallVertices[nextIndex], smallVertices[i])); edgeLines.Add(new Tuple(smallVertices[i], largeVertices[i])); } var mesh = meshBuilder.ToMeshGeometry3D(); var material = new PBRMaterial { AlbedoColor = new Color4(0.11f, 0.56f, 1f, 0.6f), // 纯白色 MetallicFactor = 0.0, RoughnessFactor = 0.01, ReflectanceFactor = 1.0, ClearCoatStrength = 1.0, ClearCoatRoughness = 0.0, // RenderEnvironmentMap = true, // IrradianceMap = TextureModel.Create(stream), // 环境贴图 }; // 设置材质的采样器状态 material.SurfaceMapSampler = new SamplerStateDescription { Filter = Filter.MinMagMipLinear, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap }; //边框 var lineBuilder = new LineBuilder(); foreach (var line in edgeLines) { lineBuilder.AddLine(line.Item1, line.Item2); } var edgeLinesModel = new LineGeometryModel3D { Geometry = lineBuilder.ToLineGeometry3D(), Color = System.Windows.Media.Colors.Black, Thickness = 1.0 }; viewport.Items.Add(edgeLinesModel); var geometryModel = new MeshGeometryModel3D { Geometry = mesh, Material = material }; viewport.Items.Add(geometryModel); viewport.ZoomExtents(); } public static void GenerateModelByEntity(Viewport3DX viewport, List entities) { var meshBuilder = new MeshBuilder(true, false); viewport.RenderHost.MSAA = MSAALevel.Maximum; foreach (var entity in entities) { List vertices = new List(){entity.Point1, entity.Point2, entity.Point3}; meshBuilder.AddPolygon(vertices); } var mesh = meshBuilder.ToMeshGeometry3D(); var material = new PBRMaterial { AlbedoColor = new Color4(0.11f, 0.56f, 1f, 0.6f), MetallicFactor = 0.0, RoughnessFactor = 0.01, ReflectanceFactor = 1.0, ClearCoatStrength = 1.0, ClearCoatRoughness = 0.0, }; // 设置材质的采样器状态 material.SurfaceMapSampler = new SamplerStateDescription { Filter = Filter.MinMagMipLinear, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap }; var geometryModel = new MeshGeometryModel3D { Geometry = mesh, Material = material }; viewport.Items.Add(geometryModel); } /// /// 传入一个三角形,找到关联面 /// /// 控件 /// 三角形顶点 /// 选择主体颜色 /// 选择关联颜色(null不渲染) /// 选择边框颜色(null不渲染) /// public static List HighlightAssociatedFaces(Viewport3DX viewport, Tuple triangleIndex, Color4 selColor, Color4? linkColor = null, Color4? borderColor = null) { List result = new List(); //选中平面上的点(实现选中高亮、边框线) List selFaceVector = new List(); selFaceVector.Add(triangleIndex.Item1); selFaceVector.Add(triangleIndex.Item2); selFaceVector.Add(triangleIndex.Item3); foreach (var item in viewport.Items) { if (item is MeshGeometryModel3D geometryModel) { var geometry = geometryModel.Geometry; if (geometry != null) { var positions = geometry.Positions; // 顶点列表 var indices = geometry.Indices; // 索引列表 // 确保 Indices 是三角形格式 for (int i = 0; i < indices.Count; i += 3) { // 获取三角形的三个顶点索引 int index0 = indices[i]; int index1 = indices[i + 1]; int index2 = indices[i + 2]; // 获取三角形的三个顶点坐标 var vertex0 = positions[index0]; var vertex1 = positions[index1]; var vertex2 = positions[index2]; List temp = new List() { vertex0, vertex1, vertex2 }; if (ArePointsOnSamePlane(triangleIndex, temp)) { //所有点共面,两种情况,三角形自己,三角形相连在同一个平面 if (temp.Count == 3 && temp.Contains(triangleIndex.Item1) && temp.Contains(triangleIndex.Item2) && temp.Contains(triangleIndex.Item3)) continue; foreach (var vector in temp) { // if(!selFaceVector.Contains(vector)) selFaceVector.Add(vector); } } } } } } //当前选中的平面 var meshBuilder = new MeshBuilder(true, false); List fanNormals2 = new List(); foreach (var vertex in selFaceVector) fanNormals2.Add(new Vector3(0, 1, 0)); meshBuilder.AddTriangleFan(selFaceVector, fanNormals2); var mesh = meshBuilder.ToMeshGeometry3D(); var material = new PBRMaterial { AlbedoColor = selColor, // 纯白色 MetallicFactor = 0, RoughnessFactor = 1, ReflectanceFactor = 0.5, }; var geometryModel1 = new MeshGeometryModel3D { Geometry = mesh, Material = material }; //当前选中平面边框线 var edgeLines = new List>(); var lengths = new List(); // 长度列表 var angles = new List(); // 夹角列表 HashSet uniqueVectors = new HashSet(selFaceVector); List sortedVectors = uniqueVectors.ToList(); Vector3 center = GetCentroid(sortedVectors); // 按极角顺时针排序 sortedVectors.Sort((v1, v2) => { double angle1 = Math.Atan2(v1.Z - center.Z, v1.X - center.X); // 计算第一个点的角度 double angle2 = Math.Atan2(v2.Z - center.Z, v2.X - center.X); // 计算第二个点的角度 return angle1.CompareTo(angle2); // 按角度从小到大排序 }); for (int i = 0; i < sortedVectors.Count - 1; i++) { var nowItem = sortedVectors[i]; var nextItem = sortedVectors[i + 1]; edgeLines.Add(new Tuple(nowItem, nextItem)); // 计算当前线段的长度 lengths.Add((nextItem - nowItem).Length()); } edgeLines.Add(new Tuple(sortedVectors.Last(), sortedVectors.First())); lengths.Add((sortedVectors.Last() - sortedVectors.First()).Length()); //计算夹角 for (int i = 0; i < sortedVectors.Count; i++) { int prevIndex = (i - 1 + sortedVectors.Count) % sortedVectors.Count; int nextIndex = (i + 1) % sortedVectors.Count; // 获取三点 var p1 = sortedVectors[prevIndex]; var p2 = sortedVectors[i]; var p3 = sortedVectors[nextIndex]; // 计算夹角 var v1 = p2 - p1; var v2 = p3 - p2; double angle = AngleBetween(v1, v2); angles.Add(angle); // 夹角,单位是度 } if (sortedVectors.Count == lengths.Count && sortedVectors.Count == angles.Count) { for (int i = 0; i < sortedVectors.Count; i++) { var point = sortedVectors[i]; var pointNext = i + 1 >= sortedVectors.Count ? sortedVectors.First() : sortedVectors[i + 1]; var lengthText = $"L: {lengths[i]:F2}"; var angleText = $"Angle: {angles[i]:F2}°"; var lengthTextPoint = (pointNext + point) / 2; // 在合适位置显示文本 var text3DL = DisplayText3D(lengthText, new Vector3(lengthTextPoint.X, lengthTextPoint.Y + 0.5f, lengthTextPoint.Z)); var text3DA = DisplayText3D(angleText, new Vector3(point.X, point.Y + 0.5f, point.Z + 0.5f)); viewport.Items.Add(text3DL); viewport.Items.Add(text3DA); result.Add(text3DL); result.Add(text3DA); } } else { // 如果列表长度不一致,给出提示信息 Console.WriteLine("The lengths or angles list size does not match the sorted vectors list."); } var lineBuilder = new LineBuilder(); foreach (var line in edgeLines) { lineBuilder.AddLine(line.Item1, line.Item2); } var edgeLinesModel = new LineGeometryModel3D { Geometry = lineBuilder.ToLineGeometry3D(), Color = System.Windows.Media.Colors.Red, Thickness = 1.0 }; viewport.Items.Add(edgeLinesModel); viewport.Items.Add(geometryModel1); result.Add(geometryModel1); result.Add(edgeLinesModel); return result; } /// /// 遍历视图中的所有三角形 /// /// /// public static List> ForeachViewPortTriangle(Viewport3DX viewport) { List> result = new List>(); foreach (var item in viewport.Items) { if (item is MeshGeometryModel3D geometryModel) { var geometry = geometryModel.Geometry; if (geometry != null) { var positions = geometry.Positions; // 顶点列表 var indices = geometry.Indices; // 索引列表 // 确保 Indices 是三角形格式 for (int i = 0; i < indices.Count; i += 3) { // 获取三角形的三个顶点索引 int index0 = indices[i]; int index1 = indices[i + 1]; int index2 = indices[i + 2]; // 获取三角形的三个顶点坐标 var vertex0 = positions[index0]; var vertex1 = positions[index1]; var vertex2 = positions[index2]; result.Add(new Tuple(vertex0, vertex1, vertex2)); } } } } return result; } private static BillboardTextModel3D DisplayText3D(string text, Vector3 position) { var billboardTextModel = new BillboardTextModel3D(); var billboardText = new BillboardText3D(); billboardText.TextInfo.Add(new TextInfo(text, position) { Foreground = Color.Green, Scale = 0.5f, }); billboardTextModel.Geometry = billboardText; return billboardTextModel; } public static float AngleBetween(Vector3 v1, Vector3 v2) { // 计算两个向量的点积 float dotProduct = Vector3.Dot(v1, v2); // 计算每个向量的模长 float magnitudeV1 = v1.Length(); float magnitudeV2 = v2.Length(); // 防止除以零错误 if (magnitudeV1 == 0 || magnitudeV2 == 0) { return 0f; } // 计算夹角的余弦值 float cosTheta = dotProduct / (magnitudeV1 * magnitudeV2); // 限制cosTheta的值范围在 -1 到 1 之间,以防计算机浮点误差 cosTheta = Math.Max(-1f, Math.Min(1f, cosTheta)); // 返回角度,单位是度 return (float)(Math.Acos(cosTheta) * (180.0 / Math.PI)); } // 计算点集合的中心点(几何质心) private static Vector3 GetCentroid(List vectors) { float x = vectors.Sum(v => v.X) / vectors.Count; float y = vectors.Sum(v => v.Y) / vectors.Count; float z = vectors.Sum(v => v.Z) / vectors.Count; return new Vector3(x, y, z); } /// /// 判断集合元素是狗共面 /// /// /// /// public static bool ArePointsOnSamePlane(Tuple triangle, List points) { // 获取三角形的三个点 var P1 = triangle.Item1; var P2 = triangle.Item2; var P3 = triangle.Item3; // 计算平面的法线 Vector3 V1 = P2 - P1; // 向量P1P2 Vector3 V2 = P3 - P1; // 向量P1P3 Vector3 normal = Vector3.Cross(V1, V2); // 叉积得到平面法线 // 对每个点进行检查 foreach (var point in points) { if (!IsPointInPlane(P1, normal, point)) { return false; // 如果有点不在平面上,返回false } } return true; // 如果所有点都在平面上,返回true } /// /// 判断点是否在平面上 /// /// private static bool IsPointInPlane(Vector3 planePoint, Vector3 normal, Vector3 point) { // 计算点到平面的距离 // 点到平面的距离公式:d = (point - planePoint) · normal / |normal| float distance = Vector3.Dot(point - planePoint, normal) / normal.Length(); // 如果距离为0,说明点在平面上 return Math.Abs(distance) < 1e-6; // 容忍一个小的误差 } public static void GenerateLightingForModel(Viewport3DX viewport) { // 移除现有的灯光 List lights = new List(); foreach (var item in viewport.Items) { if (item is Light3D) { lights.Add(item as Light3D); } } lights.ForEach(item => viewport.Items.Remove(item)); // 获取场景中最大的模型 var models = viewport.Items.OfType(); if (!models.Any()) return; var largestModel = models .OrderByDescending(m => GetBoundingBoxVolume(m.Geometry.Bound)) .FirstOrDefault(); if (largestModel == null) return; // 获取最大模型的边界立方体 var boundingBox = largestModel.Geometry.Bound; var size = boundingBox.Size; var center = boundingBox.Center; // 计算 Bounding Box 的八个顶点 var corners = new List { boundingBox.Minimum.ToPoint3D(), // 左下后 new Point3D(boundingBox.Maximum.X, boundingBox.Minimum.Y, boundingBox.Minimum.Z), // 右下后 new Point3D(boundingBox.Minimum.X, boundingBox.Maximum.Y, boundingBox.Minimum.Z), // 左上后 new Point3D(boundingBox.Minimum.X, boundingBox.Minimum.Y, boundingBox.Maximum.Z), // 左下前 boundingBox.Maximum.ToPoint3D(), // 右上前 new Point3D(boundingBox.Maximum.X, boundingBox.Maximum.Y, boundingBox.Minimum.Z), // 右上后 new Point3D(boundingBox.Minimum.X, boundingBox.Maximum.Y, boundingBox.Maximum.Z), // 左上前 new Point3D(boundingBox.Maximum.X, boundingBox.Minimum.Y, boundingBox.Maximum.Z) // 右下前 }; // 在每个顶点处添加光源 for (int i = 0; i < corners.Count; i++) { var corner = corners[i]; // 设置点光源的颜色和强度,使得八个点的光源具有不同效果 var color = i % 2 == 0 ? Colors.LightGoldenrodYellow : Colors.LightSkyBlue; // 添加点光源 var pointLight = new PointLight3D { Position = corner, Color = color, Range = (float)size.Length() * 4 // 光照范围覆盖整个模型 }; viewport.Items.Add(pointLight); } // 添加顶部的多处光源(从上向下照射,模拟太阳光) double topLightDistance = size.Y * 1.2; // 适当调整顶部光源的高度 var topLightPositions = new List { new Point3D(center.X, boundingBox.Maximum.Y + topLightDistance, center.Z), new Point3D(boundingBox.Minimum.X - size.X / 3, boundingBox.Maximum.Y + topLightDistance, center.Z), new Point3D(boundingBox.Maximum.X + size.X / 3, boundingBox.Maximum.Y + topLightDistance, center.Z) }; foreach (var position in topLightPositions) { var topLight = new PointLight3D { Position = position, Color = Colors.LightGoldenrodYellow, Range = (float)size.Length() * 0.5, // 增加光的照射范围 Attenuation = new Vector3D(1, 0.1f, 0.05f) // 控制光的衰减,使光在距离内更有效 }; viewport.Items.Add(topLight); } // 添加环境光以柔化整体效果并增加亮度 var ambientLight = new AmbientLight3D { Color = Colors.LightGray }; viewport.Items.Add(ambientLight); var ambientLigh1t = new AmbientLight3D { Color = Colors.Gray // 设置环境光颜色 }; viewport.Items.Add(ambientLigh1t); // 添加环境光以柔化整体效果并增加亮度 // var ambientLight = new AmbientLight3D // { // Color = Colors.LightGray // }; // viewport.Items.Add(ambientLight); // 添加底部的光源(从底部中心向上照射) // var bottomLightPosition = new Point3D(center.X, boundingBox.Minimum.Y - 1.0f, center.Z); // 位于底部中心点下方 // var bottomLight = new PointLight3D // { // Position = bottomLightPosition, // Color = Colors.White, // 白色光源 // Range = (float)size.Length() * 2 // 设置光照范围 // }; // viewport.Items.Add(bottomLight); } // 辅助方法:获取边界体积 private static double GetBoundingBoxVolume(BoundingBox bound) { var size = bound.Size; return size.X * size.Y * size.Z; } public static List InitDemo(Viewport3DX viewport) { List entities = new List(); try { using (FileStream fileStream = new FileStream(AppDomain.CurrentDomain.BaseDirectory + @"Resource\DimDemo.txt", FileMode.Open, FileAccess.Read)) using (StreamReader reader = new StreamReader(fileStream)) { string line; while ((line = reader.ReadLine()) != null) { if(string.IsNullOrWhiteSpace(line))continue; if(line.StartsWith("-- "))continue; entities.Add(Viewport3DTriangleEntity.CreateByString(line)); } } } catch (Exception ex) { Console.WriteLine($"读取文件时出错:{ex.Message}"); } GenerateModelByEntity(viewport, entities); GenerateLightingForModel(viewport); return entities; } public static List GenerateEmissiveModelByEntity(Viewport3DX viewport, List entities, Color4 emissiveColor) { // 创建 MeshBuilder,启用法线生成 var meshBuilder = new MeshBuilder(true, false); viewport.RenderHost.MSAA = MSAALevel.Maximum; // 通过实体构建三角形面 foreach (var entity in entities) { List vertices = new List() { entity.Point1, entity.Point2, entity.Point3 }; meshBuilder.AddPolygon(vertices); } // 生成网格 var mesh = meshBuilder.ToMeshGeometry3D(); // 创建自发光材质 var material = new PBRMaterial { AlbedoColor = new Color4(0, 0, 0, 1.0f), // 黑色,避免其他光照影响 EmissiveColor = emissiveColor, // 自发光颜色 MetallicFactor = 0.0, // 非金属 RoughnessFactor = 1.0, // 高粗糙度,避免反射效果 ReflectanceFactor = 0.0, // 无反射 ClearCoatStrength = 0.0, // 无清漆效果 ClearCoatRoughness = 1.0, // 高粗糙度 }; // 设置材质的采样器状态 material.SurfaceMapSampler = new SamplerStateDescription { Filter = Filter.MinMagMipLinear, AddressU = TextureAddressMode.Wrap, AddressV = TextureAddressMode.Wrap, AddressW = TextureAddressMode.Wrap }; // 创建几何模型 var geometryModel = new MeshGeometryModel3D { Geometry = mesh, Material = material }; // 将几何模型添加到视图中 viewport.Items.Add(geometryModel); List result = new List(); result.Add(geometryModel); return result; } public static List GenerateLineTextModelByEntity(Viewport3DX viewport, List entities) { List result = new List(); List selFaceVector = new List(); foreach (var entity in entities) { selFaceVector.Add(entity.Point1); selFaceVector.Add(entity.Point2); selFaceVector.Add(entity.Point3); } var edgeLines = new List>(); var lengths = new List(); // 长度列表 var angles = new List(); // 夹角列表 HashSet uniqueVectors = new HashSet(selFaceVector); List sortedVectors = uniqueVectors.ToList(); Vector3 center = GetCentroid(sortedVectors); sortedVectors.Sort((v1, v2) => { double angle1 = Math.Atan2(v1.Z - center.Z, v1.X - center.X); // 计算第一个点的角度 double angle2 = Math.Atan2(v2.Z - center.Z, v2.X - center.X); // 计算第二个点的角度 return angle1.CompareTo(angle2); // 按角度从小到大排序 }); for (int i = 0; i < sortedVectors.Count - 1; i++) { var nowItem = sortedVectors[i]; var nextItem = sortedVectors[i + 1]; edgeLines.Add(new Tuple(nowItem, nextItem)); // 计算当前线段的长度 lengths.Add((nextItem - nowItem).Length()); } edgeLines.Add(new Tuple(sortedVectors.Last(), sortedVectors.First())); lengths.Add((sortedVectors.Last() - sortedVectors.First()).Length()); //计算夹角 for (int i = 0; i < sortedVectors.Count; i++) { int prevIndex = (i - 1 + sortedVectors.Count) % sortedVectors.Count; int nextIndex = (i + 1) % sortedVectors.Count; // 获取三点 var p1 = sortedVectors[prevIndex]; var p2 = sortedVectors[i]; var p3 = sortedVectors[nextIndex]; // 计算夹角 var v1 = p2 - p1; var v2 = p3 - p2; double angle = AngleBetween(v1, v2); angles.Add(angle); // 夹角,单位是度 } if (sortedVectors.Count == lengths.Count && sortedVectors.Count == angles.Count) { for (int i = 0; i < sortedVectors.Count; i++) { var point = sortedVectors[i]; var pointNext = i + 1 >= sortedVectors.Count ? sortedVectors.First() : sortedVectors[i + 1]; var lengthText = $"L: {lengths[i]:F2}"; var angleText = $"Angle: {angles[i]:F2}°"; var lengthTextPoint = (pointNext + point) / 2; // 在合适位置显示文本 var text3DL = DisplayText3D(lengthText, new Vector3(lengthTextPoint.X, lengthTextPoint.Y + 0.5f, lengthTextPoint.Z)); var text3DA = DisplayText3D(angleText, new Vector3(point.X, point.Y + 0.5f, point.Z + 0.5f)); viewport.Items.Add(text3DL); viewport.Items.Add(text3DA); result.Add(text3DL); result.Add(text3DA); } } else { // 如果列表长度不一致,给出提示信息 Console.WriteLine("The lengths or angles list size does not match the sorted vectors list."); } var lineBuilder = new LineBuilder(); foreach (var line in edgeLines) { lineBuilder.AddLine(line.Item1, line.Item2); } var edgeLinesModel = new LineGeometryModel3D { Geometry = lineBuilder.ToLineGeometry3D(), Color = System.Windows.Media.Colors.Red, Thickness = 1.0 }; viewport.Items.Add(edgeLinesModel); result.Add(edgeLinesModel); return result; } }