| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- using netDxf.Entities;
- using OxyPlot;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
- using WpfLine = System.Windows.Shapes.Line;
- using DxfLine = netDxf.Entities.Line;
- using DxfLwPolyline = netDxf.Entities.Polyline2D;
- using Microsoft.Win32;
- using System.Drawing;
- using TeamAAS_VP.Resources.Languages;
- namespace TeamAAS_VP.DxfModule
- {
- /// <summary>
- /// DxfView.xaml 的交互逻辑
- /// </summary>
- public partial class DxfView : UserControl
- {
- private DxfParserService _dxfParser;
- private InteractionController _interactionController;
- private double _currentScale = 1.0;
- private double _zoomFactor = 1.2;
- public DxfView()
- {
- InitializeComponent();
- _dxfParser = new DxfParserService();
- InitializeInteractionController();
- }
- /// <summary>
- /// 选中的点位
- /// </summary>
- public List<PointF> Points { get; private set; }=new List<PointF>();
- /// <summary>
- /// 需要导入的点位表开始点编号
- /// </summary>
- public int StartIndex { get;private set; } = 1;
- private void InitializeInteractionController()
- {
- _interactionController = new InteractionController(DrawingCanvas);
- _interactionController.SelectionChanged += OnSelectionChanged;
- }
- private void BtnLoadDxf_Click(object sender, RoutedEventArgs e)
- {
- var openFileDialog = new OpenFileDialog
- {
- Filter = "DXF文件 (*.dxf)|*.dxf|所有文件 (*.*)|*.*",
- Title = Lang.选择DXF文件
- };
- if (openFileDialog.ShowDialog() == true)
- {
- LoadDxfFile(openFileDialog.FileName);
- }
- }
- private void LoadDxfFile(string filePath)
- {
- TxtStatus.Text = Lang.正在加载DXF文件;
- DrawingCanvas.Children.Clear();
- _interactionController?.ClearSelection();
- if (_dxfParser.LoadDxfFile(filePath))
- {
- var entities = _dxfParser.GetAllEntities();
- TxtStatus.Text = $"成功加载: {System.IO.Path.GetFileName(filePath)}";
- TxtEntityCount.Text = $"实体数: {entities.Count}";
- // Calculate bounds and scale
- var bounds = _dxfParser.GetBounds();
- double boundsWidth = bounds.maxX - bounds.minX;
- double boundsHeight = bounds.maxY - bounds.minY;
- if (boundsWidth > 0 && boundsHeight > 0)
- {
- double scaleX = (DrawingCanvas.Width * 0.8) / boundsWidth;
- double scaleY = (DrawingCanvas.Height * 0.8) / boundsHeight;
- _currentScale = Math.Min(scaleX, scaleY);
- }
- // Render entities
- foreach (var entity in entities)
- {
- var renderedElements = GraphicsRenderer.RenderEntity(entity, _currentScale);
- foreach (var element in renderedElements)
- {
- DrawingCanvas.Children.Add(element);
- }
- }
- // Center the drawing
- CenterDrawing(bounds);
- // Update statistics
- UpdateStatistics();
- }
- else
- {
- TxtStatus.Text = Lang.加载DXF文件失败;
- MessageBox.Show(Lang.无法加载DXF文件请检查文件是否有效, Lang.错误, MessageBoxButton.OK, MessageBoxImage.Error);
- }
- }
- private void CenterDrawing((double minX, double minY, double maxX, double maxY) bounds)
- {
- double centerX = (bounds.minX + bounds.maxX) / 2 * _currentScale;
- double centerY = -(bounds.minY + bounds.maxY) / 2 * _currentScale;
- double offsetX = DrawingCanvas.Width / 2 - centerX;
- double offsetY = DrawingCanvas.Height / 2 - centerY;
- // Translate all elements
- foreach (UIElement element in DrawingCanvas.Children)
- {
- if (element is WpfLine line)
- {
- line.X1 += offsetX;
- line.X2 += offsetX;
- line.Y1 += offsetY;
- line.Y2 += offsetY;
- }
- else if (element is FrameworkElement fe)
- {
- double left = Canvas.GetLeft(fe);
- double top = Canvas.GetTop(fe);
- Canvas.SetLeft(fe, left + offsetX);
- Canvas.SetTop(fe, top + offsetY);
- }
- }
- }
- private void UpdateStatistics()
- {
- var stats = _dxfParser.GetEntityStatistics();
- var sb = new StringBuilder();
- foreach (var stat in stats.OrderByDescending(s => s.Value))
- {
- if (stat.Value > 0)
- {
- sb.AppendLine($"{stat.Key}: {stat.Value}");
- }
- }
- TxtStatistics.Text = sb.ToString();
- }
- private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- TxtSelectedCount.Text = string.Format(Lang.选中0,e.SelectedEntities.Count);
- var sb = new StringBuilder();
- if (e.SelectedEntities.Count > 0)
- {
- sb.AppendLine(string.Format(Lang.已选择0个图形实体, e.SelectedEntities.Count)+"\n");
- var grouped = e.SelectedEntities.GroupBy(entity => entity.Type.ToString());
- foreach (var group in grouped)
- {
- sb.AppendLine($"{group.Key}: {group.Count()}");
- }
- sb.AppendLine("\n"+Lang.详细信息);
- int index = 1;
- foreach (var entity in e.SelectedEntities.Take(10))
- {
- sb.AppendLine($"\n[{index}] {GetEntityInfo(entity)}");
- index++;
- }
- if (e.SelectedEntities.Count > 10)
- {
- sb.AppendLine("\n" + string.Format(Lang.还有0个实体, e.SelectedEntities.Count - 10));
- }
- //如果选中的是圆,则输出圆心坐标,如果是直线,则输出中心点坐标,如果是矩形,则输出中心点坐标,如果是点,则输出点坐标,如果是圆弧,则输出中心点坐标
- if (Points!=null)
- {
- Points = new List<PointF>();
- }
- Points.Clear();
- foreach (var item in e.SelectedEntities)
- {
- if (item is Circle circle)
- {
- Points.Add(new PointF((float)circle.Center.X, (float)circle.Center.Y));
- }
- else if (item is DxfLine line)
- {
- float centerX = (float)((line.StartPoint.X + line.EndPoint.X) / 2);
- float centerY = (float)((line.StartPoint.Y + line.EndPoint.Y) / 2);
- Points.Add(new PointF(centerX, centerY));
- }
- else if (item is DxfLwPolyline polyline && polyline.IsClosed)
- {
- //计算多边形中心点
- float centerX = 0;
- float centerY = 0;
- foreach (var vertex in polyline.Vertexes)
- {
- centerX += (float)vertex.Position.X;
- centerY += (float)vertex.Position.Y;
- }
- centerX /= polyline.Vertexes.Count;
- centerY /= polyline.Vertexes.Count;
- Points.Add(new PointF(centerX, centerY));
- }
- else if (item is netDxf.Entities.Point point)
- {
- Points.Add(new PointF((float)point.Position.X, (float)point.Position.Y));
- }
- else if (item is Arc arc)
- {
- Points.Add(new PointF((float)arc.Center.X, (float)arc.Center.Y));
- }
- }
- }
- else
- {
- sb.AppendLine(Lang.未选择任何图形);
- }
- TxtSelectedInfo.Text = sb.ToString();
- }
- private string GetEntityInfo(EntityObject entity)
- {
- switch (entity)
- {
- case DxfLine line:
- return $"{Lang.直线} ({line.StartPoint.X:F2}, {line.StartPoint.Y:F2}) → ({line.EndPoint.X:F2}, {line.EndPoint.Y:F2})";
- case Circle circle:
- return $"{Lang.圆中心}({circle.Center.X:F2}, {circle.Center.Y:F2}), {Lang.半径}{circle.Radius:F2}";
- case Arc arc:
- return $"{Lang.圆弧中心}({arc.Center.X:F2}, {arc.Center.Y:F2}), {Lang.半径}{arc.Radius:F2}, {Lang.角度等于}{arc.StartAngle:F1}°~{arc.EndAngle:F1}°";
- case Text text:
- return $"{Lang.文本} \"{text.Value}\" {Lang.位置}({text.Position.X:F2}, {text.Position.Y:F2})";
- case MText mtext:
- return $"{Lang.多行文本} \"{mtext.Value}\" {Lang.位置}({mtext.Position.X:F2}, {mtext.Position.Y:F2})";
- case DxfLwPolyline lwPoly:
- return $"{Lang.轻量多段线} {lwPoly.Vertexes.Count} {Lang.个顶点}{(lwPoly.IsClosed ? Lang.闭合 : "")}";
- case Spline spline:
- // return $"样条曲线: {spline.ControlPoints.Count} 个控制点";
- default:
- return $"{entity.Type}: {entity.CodeName}";
- }
- }
- private void BtnClearSelection_Click(object sender, RoutedEventArgs e)
- {
- Points?.Clear();
- _interactionController?.ClearSelection();
- }
- private void BtnExtractSelected_Click(object sender, RoutedEventArgs e)
- {
- var selectedEntities = _interactionController?.GetSelectedEntities();
- if (selectedEntities == null || selectedEntities.Count == 0)
- {
- MessageBox.Show(Lang.请先选择要提取的图形, Lang.提示, MessageBoxButton.OK, MessageBoxImage.Information);
- return;
- }
- var sb = new StringBuilder();
- sb.AppendLine(string.Format(Lang.提取的图形信息共0个, selectedEntities.Count) +"\n");
-
- var grouped = selectedEntities.GroupBy(p => p.Type.ToString());
- foreach (var group in grouped)
- {
- sb.AppendLine($"\n{group.Key} ({group.Count()} {Lang.个}):");
- sb.AppendLine(new string('-', 50));
- foreach (var entity in group)
- {
- sb.AppendLine(GetEntityInfo(entity));
- }
- }
- MessageBox.Show(sb.ToString(), Lang.提取的图形信息, MessageBoxButton.OK, MessageBoxImage.Information);
- }
- private void BtnZoomIn_Click(object sender, RoutedEventArgs e)
- {
- // Smooth zoom centered at viewport center
- var sv = ScrollViewer;
- if (sv != null)
- {
- double prevScale = CanvasScaleTransform.ScaleX;
- double target = prevScale * _zoomFactor;
- target = Math.Max(0.05, Math.Min(target, 50.0));
- AnimateScale(target, null);
- }
- else
- {
- Zoom(_zoomFactor);
- }
- }
- private void BtnZoomOut_Click(object sender, RoutedEventArgs e)
- {
- var sv = ScrollViewer;
- if (sv != null)
- {
- double prevScale = CanvasScaleTransform.ScaleX;
- double target = prevScale / _zoomFactor;
- target = Math.Max(0.05, Math.Min(target, 50.0));
- AnimateScale(target, null);
- }
- else
- {
- Zoom(1.0 / _zoomFactor);
- }
- }
- private void BtnFitToScreen_Click(object sender, RoutedEventArgs e)
- {
- var sv = ScrollViewer;
- if (sv == null)
- {
- CanvasScaleTransform.ScaleX = 1.0;
- CanvasScaleTransform.ScaleY = 1.0;
- UpdateZoomDisplay();
- return;
- }
- // Compute scale to fit the canvas into viewport with some margin
- double marginFactor = 0.95;
- double targetScaleX = (sv.ViewportWidth * marginFactor) / DrawingCanvas.Width;
- double targetScaleY = (sv.ViewportHeight * marginFactor) / DrawingCanvas.Height;
- double target = Math.Min(targetScaleX, targetScaleY);
- if (double.IsNaN(target) || target <= 0)
- target = 1.0;
- target = Math.Max(0.05, Math.Min(target, 50.0));
- // Animate to target and center
- AnimateScale(target, null);
- }
- private void DrawingCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
- {
- // Zoom centered at current mouse position inside the ScrollViewer viewport
- var sv = ScrollViewer;
- if (sv == null) // fallback to previous behavior
- {
- double zoom1 = e.Delta > 0 ? _zoomFactor : 1.0 / _zoomFactor;
- Zoom(zoom1);
- return;
- }
- // Mouse position relative to the ScrollViewer (viewport)
- System.Windows.Point mousePosInViewport = e.GetPosition(sv);
- // Mouse position relative to the content (unscaled content coordinates)
- System.Windows.Point mousePosInContent = e.GetPosition(DrawingCanvas);
- double prevScale = CanvasScaleTransform.ScaleX;
- double zoom = e.Delta > 0 ? _zoomFactor : 1.0 / _zoomFactor;
- double newScale = prevScale * zoom;
- // Clamp scale to reasonable range
- newScale = Math.Max(0.05, Math.Min(newScale, 50.0));
- // Absolute position of the content point after scaling
- double absX = mousePosInContent.X * newScale;
- double absY = mousePosInContent.Y * newScale;
- // Apply scale
- CanvasScaleTransform.ScaleX = newScale;
- CanvasScaleTransform.ScaleY = newScale;
- // Ensure layout updated so ScrollViewer extents are refreshed
- sv.UpdateLayout();
- // Calculate target offsets so that the content point stays under the mouse cursor
- double targetOffsetX = absX - mousePosInViewport.X;
- double targetOffsetY = absY - mousePosInViewport.Y;
- // Clamp offsets to valid scrollable range
- double maxOffsetX = Math.Max(0, sv.ExtentWidth - sv.ViewportWidth);
- double maxOffsetY = Math.Max(0, sv.ExtentHeight - sv.ViewportHeight);
- targetOffsetX = Math.Max(0, Math.Min(targetOffsetX, maxOffsetX));
- targetOffsetY = Math.Max(0, Math.Min(targetOffsetY, maxOffsetY));
- sv.ScrollToHorizontalOffset(targetOffsetX);
- sv.ScrollToVerticalOffset(targetOffsetY);
- UpdateZoomDisplay();
- e.Handled = true;
- }
- /// <summary>
- /// Animate scale transform to targetScale. If anchorInContent is null, use viewport center as anchor.
- /// anchorInContent, when provided, is in unscaled canvas coordinates (content space).
- /// </summary>
- private void AnimateScale(double targetScale, System.Windows.Point? anchorInContent)
- {
- var sv = ScrollViewer;
- if (sv == null)
- {
- CanvasScaleTransform.ScaleX = targetScale;
- CanvasScaleTransform.ScaleY = targetScale;
- UpdateZoomDisplay();
- return;
- }
- double prevScale = CanvasScaleTransform.ScaleX;
- if (Math.Abs(prevScale - targetScale) < 1e-6)
- return;
- // Determine anchor in content space and its position in viewport
- System.Windows.Point anchorContent;
- System.Windows.Point anchorViewport;
- if (anchorInContent.HasValue)
- {
- anchorContent = anchorInContent.Value;
- anchorViewport = new System.Windows.Point(anchorContent.X * prevScale - sv.HorizontalOffset, anchorContent.Y * prevScale - sv.VerticalOffset);
- }
- else
- {
- // use viewport center
- anchorViewport = new System.Windows.Point(sv.ViewportWidth / 2.0, sv.ViewportHeight / 2.0);
- anchorContent = new System.Windows.Point((sv.HorizontalOffset + anchorViewport.X) / prevScale, (sv.VerticalOffset + anchorViewport.Y) / prevScale);
- }
- // Calculate target offsets so that the anchor stays in the same viewport position after scaling
- double targetOffsetX = anchorContent.X * targetScale - anchorViewport.X;
- double targetOffsetY = anchorContent.Y * targetScale - anchorViewport.Y;
- double maxOffsetX = Math.Max(0, sv.ExtentWidth - sv.ViewportWidth);
- double maxOffsetY = Math.Max(0, sv.ExtentHeight - sv.ViewportHeight);
- targetOffsetX = Math.Max(0, Math.Min(targetOffsetX, maxOffsetX));
- targetOffsetY = Math.Max(0, Math.Min(targetOffsetY, maxOffsetY));
- var duration = TimeSpan.FromMilliseconds(200);
- var animX = new DoubleAnimation(prevScale, targetScale, duration, FillBehavior.Stop) { EasingFunction = new QuadraticEase() };
- var animY = new DoubleAnimation(prevScale, targetScale, duration, FillBehavior.Stop) { EasingFunction = new QuadraticEase() };
- int completed = 0;
- EventHandler whenDone = (s, e) =>
- {
- completed++;
- if (completed >= 2)
- {
- // Ensure final values
- CanvasScaleTransform.ScaleX = targetScale;
- CanvasScaleTransform.ScaleY = targetScale;
- // Update layout so extents reflect final scale
- sv.UpdateLayout();
- // Recalculate clamped offsets based on final extents
- double maxX = Math.Max(0, sv.ExtentWidth - sv.ViewportWidth);
- double maxY = Math.Max(0, sv.ExtentHeight - sv.ViewportHeight);
- double finalOffsetX = Math.Max(0, Math.Min(targetOffsetX, maxX));
- double finalOffsetY = Math.Max(0, Math.Min(targetOffsetY, maxY));
- sv.ScrollToHorizontalOffset(finalOffsetX);
- sv.ScrollToVerticalOffset(finalOffsetY);
- UpdateZoomDisplay();
- }
- };
- animX.Completed += whenDone;
- animY.Completed += whenDone;
- CanvasScaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animX);
- CanvasScaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animY);
- }
- private void Zoom(double factor)
- {
- CanvasScaleTransform.ScaleX *= factor;
- CanvasScaleTransform.ScaleY *= factor;
- UpdateZoomDisplay();
- }
- private void UpdateZoomDisplay()
- {
- TxtZoom.Text = $"{Lang.缩放} {CanvasScaleTransform.ScaleX * 100:F0}%";
-
- }
- private void TxtStartIndex_ValueChanged(object sender, RoutedPropertyChangedEventArgs<int> e)
- {
- StartIndex= e.NewValue;
- }
- }
- }
|