diff --git a/Views/UserControl/Viewport3D.xaml b/Views/UserControl/Viewport3D.xaml index fca505d..0a52865 100644 --- a/Views/UserControl/Viewport3D.xaml +++ b/Views/UserControl/Viewport3D.xaml @@ -23,7 +23,8 @@ - + @@ -33,7 +34,8 @@ - + @@ -43,7 +45,8 @@ - + @@ -82,20 +85,24 @@ - - - - - + + + + + - - - - - + + + + + - + @@ -115,7 +122,7 @@ - + @@ -124,34 +131,44 @@ - + - + - + - + - - - - + + + + + + + + + + diff --git a/Views/UserControl/Viewport3D.xaml.cs b/Views/UserControl/Viewport3D.xaml.cs index 01bb24c..c43d73e 100644 --- a/Views/UserControl/Viewport3D.xaml.cs +++ b/Views/UserControl/Viewport3D.xaml.cs @@ -2,6 +2,7 @@ using System.Text.Json.Nodes; using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using System.Windows.Media.Media3D; using HandyControl.Controls; using HelixToolkit.Wpf.SharpDX; using SharpDX; @@ -13,210 +14,353 @@ namespace SparkClient.Views.UserControl; public partial class Viewport3D { - // 注册一个依赖属性,类型是 Viewport3DData - public static readonly DependencyProperty ViewportDataProperty = - DependencyProperty.Register( - nameof(ViewportData), - typeof(ViewportData.ViewportData), - typeof(Viewport3D), - new PropertyMetadata(null, OnViewportDataChanged)); - // 公开的 CLR 属性包装器 - public ViewportData.ViewportData ViewportData + public Viewport3D() { - get => (ViewportData.ViewportData)GetValue(ViewportDataProperty); - set => SetValue(ViewportDataProperty, value); + InitializeComponent(); + DataContext = this; + this.Viewport3Dx.EffectsManager = new DefaultEffectsManager(); + Viewport3Dx.ShowViewCube = false; + Viewport3Dx.ShowCoordinateSystem = false; + ViewportManager.SetViewport3D(Viewport3Dx); } - // 当 ViewportData 属性发生变化时触发 - private static void OnViewportDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + /// + /// 初始化 + /// + /// + /// + private void Viewport3D_OnLoaded(object sender, RoutedEventArgs e) { - if (d is Viewport3D control && e.NewValue is ViewportData.ViewportData data) - { - control.Initialize(data); - } + ViewportManager.LoadModelByEntities(new List()); + + //选项初始化 显示-后端-管理类 一致 + ViewportRightMenuSelectFaceFrame.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuSelectFaceLengthText.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuSelectFaceAngleText.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuSelectFaceKind.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuShowModelFace.IsChecked = true; + ViewportRightMenuShowModelFrame.IsChecked = true; + ViewportRightMenuShowLighting.IsChecked = true; + ViewportRightMenuSelectFace.IsChecked = ViewportManager.DoubleClickSelect; + ViewportRightMenuSelectFaceFrame.IsChecked = ViewportManager.DoubleClickSelectShowBorder; + ViewportRightMenuSelectFaceLengthText.IsChecked = ViewportManager.DoubleClickSelectShowBorderLength; + ViewportRightMenuSelectFaceAngleText.IsChecked = ViewportManager.DoubleClickSelectShowBorderAngle; + ViewportRightMenuSelectFaceKind.IsChecked = ViewportManager.DoubleClickSelectShowPlaneType; + } - - - private void Initialize(ViewportData.ViewportData vpData) + /// + /// 双击选择面 + /// + /// + /// + private void Viewport3Dx_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { - // 根据 Viewport3DData 的内容初始化控件 - // 例如:设置控件内部的某些属性,渲染 3D 模型等 - if (!string.IsNullOrEmpty(vpData.DiamondData)) + if (ViewportManager.DoubleClickSelect == false) return; + + var mousePosition = e.GetPosition(Viewport3Dx); + var hits = Viewport3Dx.FindHits(mousePosition); + // 如果没有命中任何 3D 对象 + if (hits == null || hits.Count == 0) { - JsonNode? json = null; - try - { - json = JsonNode.Parse(vpData.DiamondData); - } - catch - { - throw new Exception("DiamondData is invalid"); - } - - if(json == null) throw new Exception("DiamondData is invalid"); + ViewportManager.ClearDicModels(); + return; + } - try + // 获取第一个命中的对象 + var hit = hits[0]; + // 检查是否是 MeshGeometryModel3D + if (hit.ModelHit is MeshGeometryModel3D modelHit) + { + // 获取几何信息 + var geometry = modelHit.Geometry as HelixToolkit.Wpf.SharpDX.MeshGeometry3D; + if (geometry != null) { - List facets = new List(); - if (json.AsObject()["facets"] != null) - { - JsonArray jsonArray = json.AsObject()["facets"].AsArray(); - foreach (var item in jsonArray) - { - var value = CommonHelper.CreateByJsonStr(item.ToString()); - facets.Add(value); - } - } - else - { - throw new Exception("facets is invalid"); - } + // 获取命中的三角形索引 + var triangleIndex = hit.TriangleIndices; - if (json.AsObject()["positive_direction"] != null) - { - JsonObject positive = json.AsObject()["positive_direction"].AsObject(); - ViewportManager.PositiveDirection.X = float.Parse(positive["x"]?.ToString() ?? "1.0"); - ViewportManager.PositiveDirection.Y = float.Parse(positive["z"]?.ToString() ?? "0"); - ViewportManager.PositiveDirection.Z = float.Parse(positive["y"]?.ToString() ?? "0"); - } - else - { - ViewportManager.PositiveDirection.X = 1.0f; - ViewportManager.PositiveDirection.Y = 0f; - ViewportManager.PositiveDirection.Z = 0; - } - - var midZ = facets.SelectMany(e => new[] { e.Point1.X, e.Point2.X, e.Point3.X }).OrderBy(z => Math.Abs(z)) - .First(); - List facetsFinal = new List(); - foreach (var item in facets) - { - var data = ViewportHelperPro.VectorClockwiseSort(new List - { item.Point1, item.Point2, item.Point3 }); - - if (item.PlaneType == PlaneType.StarFacet || item.PlaneType == PlaneType.UpperGirdleFacet || - item.PlaneType == PlaneType.UpperMainFacet || item.PlaneType == PlaneType.TableFacet) - { - item.Point1 = data[0]; - item.Point2 = data[1]; - item.Point3 = data[2]; - } - else if (item.PlaneType == PlaneType.Girdle) - { - var center = ViewportHelperPro.GetCentroid(data); - data.Sort((v1, v2) => - { - double angle1 = Math.Atan2(v1.Y - center.Y, v1.X - center.X); - double angle2 = Math.Atan2(v2.Y - center.Y, v2.X - center.X); - return angle1.CompareTo(angle2); - }); - if (center.Z >= midZ) - { - - item.Point1 = data[0]; - item.Point2 = data[1]; - item.Point3 = data[2]; - } - else - { - item.Point1 = data[2]; - item.Point2 = data[1]; - item.Point3 = data[0]; - - } - } - else - { - item.Point1 = data[2]; - item.Point2 = data[1]; - item.Point3 = data[0]; - } - - item.TriangleCode = CommonHelper.GenerateTriangleCode(item.Point1, item.Point2, item.Point3); - facetsFinal.Add(item); - } - - // ViewportManager.ViewportTriangle.AddRange(facetsFinal); - ViewportManager.LoadModelByEntities(facetsFinal); - } - catch (Exception ex) - { - throw new Exception(ex.Message); + // 获取三角形顶点 + var vertex1 = geometry.Positions[triangleIndex.Item1]; + var vertex2 = geometry.Positions[triangleIndex.Item2]; + var vertex3 = geometry.Positions[triangleIndex.Item3]; + + ViewportManager.ChooseTriangleCode = CommonHelper.GenerateTriangleCode(vertex1, vertex2, vertex3); + ViewportManager.ResetChooseAddModels(); } } + else + { + ViewportManager.ClearDicModels(); + } } - - public Viewport3D() + + /// + /// 顶部按钮 - 视角切换 + /// + /// + /// + /// + private void BtnAngle_OnClick(object sender, RoutedEventArgs e) { - InitializeComponent(); - DataContext = this; - this.Viewport3Dx.EffectsManager = new DefaultEffectsManager(); - ViewportManager.SetViewport3D(Viewport3Dx); + var directionName = ((Button)sender).Name.ToString(); + var directionValue = (int) TbCustomizeRevolve.Value; + + var center = ViewportManager.ModelBounds.Center; + var maxDimension = ViewportManager.ModelBounds.Size.Length(); + var distance = maxDimension *1.2; // 调整相机到模型的距离,保证视野范围内 + // 获取当前相机 + var camera = Viewport3Dx.Camera as HelixToolkit.Wpf.SharpDX.PerspectiveCamera; + + switch (directionName) + { + case "BtnFrontView": + //测 + camera.Position = new Point3D(center.X, center.Y, center.Z + distance); // 从前面看,Z轴正方向 + camera.UpDirection = new Vector3D(0, -1, 0); + break; + break; + case "BtnTopView": + //顶、 + camera.Position = new Point3D(center.X, center.Y - distance, center.Z); // 从底部看,Y轴负方向 + camera.UpDirection = new Vector3D(0, 0, -1); + + break; + case "BtnBottomView": + //低 + camera.Position = new Point3D(center.X, center.Y + distance, center.Z); // 从顶部看,Y轴正方向 + camera.UpDirection = new Vector3D(0, 0, 1); + break; + + } + camera.LookDirection = new Vector3D(center.X - camera.Position.X, center.Y - camera.Position.Y, center.Z - camera.Position.Z); } - - #region 菜单事件绑定 - private void MenuItem_OnChecked(object sender, RoutedEventArgs e) + + /// + /// 按钮调整相机方向[底部按钮] + /// + /// + /// + private void BtnDirection_OnClick(object sender, RoutedEventArgs e) { - Growl.InfoGlobal("Viewport is checked" + e.OriginalSource); + var directionName = ((Button)sender).Name.ToString(); + var directionValue = (int) TbCustomizeRevolve.Value; + + switch (directionName) + { + case "BtnTop": + //上 + break; + case "BtnBottom": + //下 + break; + case "BtnLeft": + //左 + break; + case "BtnRight": + //右 + break; + } } - private void MenuItem_OnUnchecked(object sender, RoutedEventArgs e) + + #region 右键菜单事件绑定 + /// + /// 右键选择 + /// + /// + /// + private void MenuItem_OnCheckedChanged(object sender, RoutedEventArgs e) { - Growl.InfoGlobal("Viewport is Unchecked" + e.OriginalSource); + ViewportRightMenuSelectFaceFrame.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuSelectFaceLengthText.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuSelectFaceAngleText.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + ViewportRightMenuSelectFaceKind.IsEnabled = ViewportRightMenuSelectFace.IsChecked ; + + var controlName = ((MenuItem)sender).Name.ToString(); + var checkResult = (bool)((MenuItem)sender).IsChecked; + switch (controlName) + { + case "ViewportRightMenuShowModelFace": + //显示模型面 + ViewportManager.ShowMainModel3D(checkResult); + break; + case "ViewportRightMenuShowModelFrame": + //显示模型边框 + ViewportManager.ShowMainModelLines(checkResult); + break; + case "ViewportRightMenuShowLighting": + //显示光照 + ViewportManager.ShowMainModelLighting(checkResult); + break; + case "ViewportRightMenuShowDefectFace": + //显示瑕疵面 + ViewportManager.MarkSpecificFaces(checkResult); + break; + case "ViewportRightMenuShowFront": + //显示正方向箭头 + ViewportManager.PointTowardsTheFront(checkResult); + break; + case "ViewportRightMenuSelectFace": + //双击选择面 + ViewportManager.DoubleClickSelect = checkResult; + ViewportManager.ClearDicModels(); + break; + case "ViewportRightMenuSelectFaceFrame": + //双击选择面的边框 + ViewportManager.DoubleClickSelectShowBorder = checkResult; + ViewportManager.ResetChooseAddModels(); + break; + case "ViewportRightMenuSelectFaceLengthText": + //双击选择面的边框长度文字 + ViewportManager.DoubleClickSelectShowBorderLength = checkResult; + ViewportManager.ResetChooseAddModels(); + break; + case "ViewportRightMenuSelectFaceAngleText": + //双击选择面的边框夹角文字 + ViewportManager.DoubleClickSelectShowBorderAngle = checkResult; + ViewportManager.ResetChooseAddModels(); + break; + case "ViewportRightMenuSelectFaceKind": + //双击选择面的选择同类面 + ViewportManager.DoubleClickSelectShowPlaneType = checkResult; + ViewportManager.ResetChooseAddModels(); + break; + } } + /// + /// 右键按钮 + /// + /// + /// private void MenuItem_OnClick(object sender, RoutedEventArgs e) { - + var controlName = ((MenuItem)sender).Name.ToString(); + switch (controlName) + { + case "ViewportRightMenuFront": + //切换到正面 + var Camear = + ViewportHelperPro.CalculateCamera(ViewportManager.PositiveDirection, ViewportManager.ModelBounds); + Viewport3Dx.Camera.Position = Camear.Position; + Viewport3Dx.Camera.LookDirection = Camear.LookDirection; + Viewport3Dx.Camera.UpDirection = new Vector3D(Camear.UpDirection.X, Camear.UpDirection.Y*-1, Camear.UpDirection.Z); + break; + case "ViewportRightMenuSaveViewToPNG": + //保存图片 + break; + } } #endregion + #region 页面隐式交互 + + public static readonly DependencyProperty ViewportDataProperty = + DependencyProperty.Register( + nameof(ViewportData), + typeof(ViewportData.ViewportData), + typeof(Viewport3D), + new PropertyMetadata(null, OnViewportDataChanged)); + + public ViewportData.ViewportData ViewportData + { + get => (ViewportData.ViewportData)GetValue(ViewportDataProperty); + set => SetValue(ViewportDataProperty, value); + } + private static void OnViewportDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is Viewport3D control && e.NewValue is ViewportData.ViewportData data) + { + control.Initialize(data); + } + } + + + private void Initialize(ViewportData.ViewportData vpData) + { + + } + private System.Windows.Point _mouseDownPosition; private bool _isDragging = false; private void UIElement_OnPreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e) { if (_isDragging) { - // 如果发生拖动,不显示菜单 - _isDragging = false; // 重置拖动状态 + _isDragging = false; MainBorder.ContextMenu.Visibility = Visibility.Hidden; - MainBorder.ContextMenu.IsOpen = false; return; } MainBorder.ContextMenu.Visibility = Visibility.Visible; - // 未拖动时,显示右键菜单 - MainBorder.ContextMenu.IsOpen = true; - // e.Handled = true; // 阻止事件继续传播 + } private void UIElement_OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) { _mouseDownPosition = e.GetPosition(Viewport3Dx); - _isDragging = false; // 重置拖动状态 - - } - #endregion - - private void Viewport3D_OnLoaded(object sender, RoutedEventArgs e) - { - //ViewportData.LoadData(); - ViewportManager.LoadModelByEntities(new List()); + _isDragging = false; + if (MainBorder.ContextMenu.Visibility == Visibility.Visible) + { + MainBorder.ContextMenu.Visibility = Visibility.Hidden; + MainBorder.ContextMenu.IsOpen = false; + } } - + private void Viewport3Dx_OnPreviewMouseMove(object sender, MouseEventArgs e) { if (e.RightButton == MouseButtonState.Pressed) { - // 计算鼠标移动距离 var currentPosition = e.GetPosition(Viewport3Dx); var distance = (currentPosition - _mouseDownPosition).Length; + MainBorder.ContextMenu.Visibility = Visibility.Hidden; + MainBorder.ContextMenu.IsOpen = false; + if (distance > 5) + { + _isDragging = true; + } + } + } + private void TbCustomizeRevolve_OnPreviewTextInput(object sender, TextCompositionEventArgs e) + { + e.Handled = !CommonHelper.IsTextNumeric(e.Text); + } + + private void TbCustomizeRevolve_OnPreviewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Tab || e.Key == Key.Left || e.Key == Key.Right) + { + e.Handled = false; // 不拦截 + } + else + { + e.Handled = !CommonHelper.IsKeyNumeric(e.Key); + } + } + + private void TbCustomizeRevolve_OnTextChanged(object sender, TextChangedEventArgs e) + { + var textBox = sender as System.Windows.Controls.TextBox; - // 判断是否达到拖动的阈值 - if (distance > 5) // 阈值可根据需要调整 + if (int.TryParse(textBox.Text, out int value)) + { + if (value < 0) { - _isDragging = true; // 标记为拖动 + textBox.Text = "0"; // 如果小于0,重置为0 } + else if (value > 360) + { + textBox.Text = "360"; // 如果大于360,重置为360 + } + textBox.SelectionStart = textBox.Text.Length; // 保持光标在文本末尾 + } + else if (!string.IsNullOrWhiteSpace(textBox.Text)) + { + textBox.Text = "0"; } } + + #endregion + + + } \ No newline at end of file diff --git a/Views/UserControl/ViewportData/Helper/CommonHelper.cs b/Views/UserControl/ViewportData/Helper/CommonHelper.cs index 83a7362..ab4ab29 100644 --- a/Views/UserControl/ViewportData/Helper/CommonHelper.cs +++ b/Views/UserControl/ViewportData/Helper/CommonHelper.cs @@ -1,5 +1,6 @@ using System.Security.Cryptography; using System.Text; +using System.Windows.Input; using Newtonsoft.Json; using SharpDX; using SparkClient.Views.UserControl.ViewportData.Entity; @@ -9,6 +10,18 @@ namespace SparkClient.Views.UserControl.ViewportData.Helper; public class CommonHelper { + + public static bool IsTextNumeric(string text) + { + return int.TryParse(text, out _); // 只允许数字输入 + } + + public static bool IsKeyNumeric(Key key) + { + // 判断按键是否是数字键 + return (key >= Key.D0 && key <= Key.D9) || (key >= Key.NumPad0 && key <= Key.NumPad9); + } + public static Viewport3DTriangleEntity CreateByJsonStr(string json) { // 解析 JSON 数据 diff --git a/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs b/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs index c4398bd..3d59451 100644 --- a/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs +++ b/Views/UserControl/ViewportData/Helper/ViewportHelperPro.cs @@ -558,11 +558,11 @@ public class ViewportHelperPro Vector3 cameraPosition, Vector3 modelCenter, BoundingBox modelBounds, - float totalLength = 2f, // 箭头总长度 + float totalLength = 1.5f, // 箭头总长度 float cylinderRatio = 0.7f,// 圆柱部分占比 - float diameter = 0.5f, // 箭头直径 - float headDiameterRatio = 1f, // 圆锥直径与圆柱直径的比例 - float padding = 2.0f + float diameter = 0.25f, // 箭头直径 + float headDiameterRatio = 2f, // 圆锥直径与圆柱直径的比例 + float padding = 1.0f ) { // 计算模型的半径(对角线的一半) diff --git a/Views/UserControl/ViewportData/Helper/ViewportManager.cs b/Views/UserControl/ViewportData/Helper/ViewportManager.cs index c38f54f..371044d 100644 --- a/Views/UserControl/ViewportData/Helper/ViewportManager.cs +++ b/Views/UserControl/ViewportData/Helper/ViewportManager.cs @@ -1,8 +1,10 @@ +using System.Windows.Media.Media3D; using HelixToolkit.Wpf.SharpDX; using SharpDX; using SharpDX.Direct3D11; using SparkClient.Views.UserControl.ViewportData.Entity; using SparkClient.Views.UserControl.ViewportData.Enum; +using GeometryModel3D = HelixToolkit.Wpf.SharpDX.GeometryModel3D; namespace SparkClient.Views.UserControl.ViewportData.Helper; @@ -152,6 +154,7 @@ public class ViewportManager MainModelLighting = ViewportHelperPro.GenerateLightingForModel(_viewport); //切换相机视角 _viewport.Camera = ViewportHelperPro.CalculateCamera(PositiveDirection, ModelBounds); + _viewport.Camera.UpDirection = new Vector3D(0, -1, 0); _viewport.RenderHost.MSAA = MSAALevel.Maximum; }