using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using DwgExtractorManual.Models; using Brushes = System.Windows.Media.Brushes; using MouseEventArgs = System.Windows.Input.MouseEventArgs; using Point = System.Windows.Point; using Rectangle = System.Windows.Shapes.Rectangle; namespace DwgExtractorManual.Views { public partial class TableCellVisualizationWindow : Window { private List _visualizationData; private TableCellVisualizationData? _currentData; private double _scale = 1.0; private const double MARGIN = 50; public TableCellVisualizationWindow(List visualizationData) { InitializeComponent(); _visualizationData = visualizationData; LoadFileList(); } private void LoadFileList() { lstFiles.ItemsSource = _visualizationData; if (_visualizationData.Count > 0) { lstFiles.SelectedIndex = 0; } } private void LstFiles_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (lstFiles.SelectedItem is TableCellVisualizationData selectedData) { _currentData = selectedData; RefreshVisualization(); UpdateInfo(); } } private void UpdateInfo() { if (_currentData == null) { txtInfo.Text = "파일을 선택하세요"; txtStatus.Text = "준비됨"; return; } txtInfo.Text = $"파일: {_currentData.FileName}\n" + $"Note: {_currentData.NoteText}\n" + $"셀 수: {_currentData.Cells.Count}\n" + $"선분 수: {_currentData.TableSegments.Count}\n" + $"텍스트 수: {_currentData.TextEntities.Count}\n" + $"교차점 수: {_currentData.IntersectionPoints.Count}\n" + $"대각선 수: {_currentData.DiagonalLines.Count}"; txtStatus.Text = $"{_currentData.FileName} - 셀 {_currentData.Cells.Count}개"; } private void RefreshVisualization() { if (_currentData == null) return; cnvVisualization.Children.Clear(); // 좌표계 변환 계산 var bounds = CalculateBounds(); if (bounds == null) return; var (minX, minY, maxX, maxY) = bounds.Value; var dataWidth = maxX - minX; var dataHeight = maxY - minY; // 캔버스 크기 설정 (여백 포함) var canvasWidth = cnvVisualization.Width; var canvasHeight = cnvVisualization.Height; // 스케일 계산 (데이터가 캔버스에 맞도록) var scaleX = (canvasWidth - 2 * MARGIN) / dataWidth; var scaleY = (canvasHeight - 2 * MARGIN) / dataHeight; _scale = Math.Min(scaleX, scaleY) * 0.9; // 여유분 10% // Note 경계 표시 if (chkShowNoteBounds.IsChecked == true) { DrawNoteBounds(minX, minY, maxX, maxY); } // 선분 표시 if (chkShowSegments.IsChecked == true) { DrawSegments(); } // 셀 경계 표시 (기존) if (chkShowCells.IsChecked == true) { DrawCells(); } // 정확한 셀 경계 표시 (새로운) if (chkShowCellBoundaries.IsChecked == true) { DrawCellBoundaries(); } // 텍스트 표시 if (chkShowTexts.IsChecked == true) { DrawTexts(); } // 교차점 표시 if (chkShowIntersections.IsChecked == true) { DrawIntersections(); } // 대각선 표시 (셀 디버깅용) if (chkShowDiagonals.IsChecked == true) { DrawDiagonals(); } } private (double minX, double minY, double maxX, double maxY)? CalculateBounds() { if (_currentData == null || _currentData.Cells.Count == 0) return null; var minX = _currentData.Cells.Min(c => c.MinX); var minY = _currentData.Cells.Min(c => c.MinY); var maxX = _currentData.Cells.Max(c => c.MaxX); var maxY = _currentData.Cells.Max(c => c.MaxY); // 선분도 고려 if (_currentData.TableSegments.Count > 0) { minX = Math.Min(minX, _currentData.TableSegments.Min(s => Math.Min(s.StartX, s.EndX))); minY = Math.Min(minY, _currentData.TableSegments.Min(s => Math.Min(s.StartY, s.EndY))); maxX = Math.Max(maxX, _currentData.TableSegments.Max(s => Math.Max(s.StartX, s.EndX))); maxY = Math.Max(maxY, _currentData.TableSegments.Max(s => Math.Max(s.StartY, s.EndY))); } // 교차점도 고려 if (_currentData.IntersectionPoints.Count > 0) { minX = Math.Min(minX, _currentData.IntersectionPoints.Min(i => i.X)); minY = Math.Min(minY, _currentData.IntersectionPoints.Min(i => i.Y)); maxX = Math.Max(maxX, _currentData.IntersectionPoints.Max(i => i.X)); maxY = Math.Max(maxY, _currentData.IntersectionPoints.Max(i => i.Y)); } // 대각선도 고려 if (_currentData.DiagonalLines.Count > 0) { minX = Math.Min(minX, _currentData.DiagonalLines.Min(d => Math.Min(d.StartX, d.EndX))); minY = Math.Min(minY, _currentData.DiagonalLines.Min(d => Math.Min(d.StartY, d.EndY))); maxX = Math.Max(maxX, _currentData.DiagonalLines.Max(d => Math.Max(d.StartX, d.EndX))); maxY = Math.Max(maxY, _currentData.DiagonalLines.Max(d => Math.Max(d.StartY, d.EndY))); } // 정확한 셀 경계도 고려 if (_currentData.CellBoundaries != null && _currentData.CellBoundaries.Count > 0) { var allCellX = _currentData.CellBoundaries.SelectMany(cb => new[] { cb.TopLeftX, cb.TopRightX, cb.BottomLeftX, cb.BottomRightX }); var allCellY = _currentData.CellBoundaries.SelectMany(cb => new[] { cb.TopLeftY, cb.TopRightY, cb.BottomLeftY, cb.BottomRightY }); minX = Math.Min(minX, allCellX.Min()); minY = Math.Min(minY, allCellY.Min()); maxX = Math.Max(maxX, allCellX.Max()); maxY = Math.Max(maxY, allCellY.Max()); } return (minX, minY, maxX, maxY); } private Point TransformPoint(double x, double y) { var bounds = CalculateBounds(); if (bounds == null) return new Point(0, 0); var (minX, minY, maxX, maxY) = bounds.Value; // CAD 좌표계 -> WPF 좌표계 변환 (Y축 뒤집기) var transformedX = (x - minX) * _scale + MARGIN; var transformedY = cnvVisualization.Height - ((y - minY) * _scale + MARGIN); return new Point(transformedX, transformedY); } private void DrawNoteBounds(double minX, double minY, double maxX, double maxY) { var topLeft = TransformPoint(minX, maxY); var bottomRight = TransformPoint(maxX, minY); var rect = new Rectangle { Width = bottomRight.X - topLeft.X, Height = bottomRight.Y - topLeft.Y, Stroke = Brushes.Red, StrokeThickness = 2, StrokeDashArray = new DoubleCollection { 5, 5 }, Fill = null }; Canvas.SetLeft(rect, topLeft.X); Canvas.SetTop(rect, topLeft.Y); cnvVisualization.Children.Add(rect); } private void DrawSegments() { if (_currentData == null) return; foreach (var segment in _currentData.TableSegments) { var startPoint = TransformPoint(segment.StartX, segment.StartY); var endPoint = TransformPoint(segment.EndX, segment.EndY); var line = new Line { X1 = startPoint.X, Y1 = startPoint.Y, X2 = endPoint.X, Y2 = endPoint.Y, Stroke = segment.IsHorizontal ? Brushes.Blue : Brushes.Green, StrokeThickness = 1 }; cnvVisualization.Children.Add(line); } } private void DrawCells() { if (_currentData == null) return; var colors = new[] { Brushes.Red, Brushes.Blue, Brushes.Green, Brushes.Purple, Brushes.Orange }; for (int i = 0; i < _currentData.Cells.Count; i++) { var cell = _currentData.Cells[i]; var topLeft = TransformPoint(cell.MinX, cell.MaxY); var bottomRight = TransformPoint(cell.MaxX, cell.MinY); var rect = new Rectangle { Width = bottomRight.X - topLeft.X, Height = bottomRight.Y - topLeft.Y, Stroke = colors[i % colors.Length], StrokeThickness = 2, Fill = null }; Canvas.SetLeft(rect, topLeft.X); Canvas.SetTop(rect, topLeft.Y); cnvVisualization.Children.Add(rect); // 셀 번호 표시 var label = new TextBlock { Text = $"R{cell.Row}C{cell.Column}", // 이미 1-based 인덱싱 적용됨 FontSize = 8, // 폰트 크기 조정 Foreground = colors[i % colors.Length], FontWeight = FontWeights.Bold }; Canvas.SetLeft(label, topLeft.X + 2); // 좌상단에 위치 + 약간의 패딩 Canvas.SetTop(label, topLeft.Y + 2); // 좌상단에 위치 + 약간의 패딩 cnvVisualization.Children.Add(label); // 셀 텍스트 표시 if (!string.IsNullOrEmpty(cell.Text)) { var textLabel = new TextBlock { Text = cell.Text, FontSize = 8, Foreground = Brushes.Black, Background = Brushes.LightYellow }; // 셀 텍스트는 셀 중앙에 표시 var centerPoint = TransformPoint(cell.CenterX, cell.CenterY); Canvas.SetLeft(textLabel, centerPoint.X - (textLabel.ActualWidth / 2)); // 텍스트 중앙 정렬 Canvas.SetTop(textLabel, centerPoint.Y - (textLabel.ActualHeight / 2)); // 텍스트 중앙 정렬 cnvVisualization.Children.Add(textLabel); } } } private void DrawCellBoundaries() { if (_currentData == null || _currentData.CellBoundaries == null) return; for (int i = 0; i < _currentData.CellBoundaries.Count; i++) { var cellBoundary = _currentData.CellBoundaries[i]; // 4개 모서리 좌표 변환 var topLeft = TransformPoint(cellBoundary.TopLeftX, cellBoundary.TopLeftY); var topRight = TransformPoint(cellBoundary.TopRightX, cellBoundary.TopRightY); var bottomLeft = TransformPoint(cellBoundary.BottomLeftX, cellBoundary.BottomLeftY); var bottomRight = TransformPoint(cellBoundary.BottomRightX, cellBoundary.BottomRightY); // 셀 경계를 Path로 그리기 (정확한 4개 모서리 연결) var pathGeometry = new PathGeometry(); var pathFigure = new PathFigure(); pathFigure.StartPoint = topLeft; pathFigure.Segments.Add(new LineSegment(topRight, true)); pathFigure.Segments.Add(new LineSegment(bottomRight, true)); pathFigure.Segments.Add(new LineSegment(bottomLeft, true)); pathFigure.IsClosed = true; pathGeometry.Figures.Add(pathFigure); var path = new Path { Data = pathGeometry, Stroke = Brushes.DarkBlue, StrokeThickness = 3, Fill = null }; cnvVisualization.Children.Add(path); // 라벨 표시 (셀 중앙) if (!string.IsNullOrEmpty(cellBoundary.Label)) { var centerX = (topLeft.X + bottomRight.X) / 2; var centerY = (topLeft.Y + bottomRight.Y) / 2; var label = new TextBlock { Text = cellBoundary.Label, FontSize = 10, Foreground = Brushes.DarkBlue, FontWeight = FontWeights.Bold, Background = Brushes.LightCyan }; Canvas.SetLeft(label, centerX - 15); // 대략적인 중앙 정렬 Canvas.SetTop(label, centerY - 6); cnvVisualization.Children.Add(label); } } } private void DrawTexts() { if (_currentData == null) return; foreach (var text in _currentData.TextEntities) { var point = TransformPoint(text.X, text.Y); var textBlock = new TextBlock { Text = text.Text, FontSize = 9, Foreground = text.IsInTable ? Brushes.DarkBlue : Brushes.Gray }; Canvas.SetLeft(textBlock, point.X); Canvas.SetTop(textBlock, point.Y); cnvVisualization.Children.Add(textBlock); // 텍스트 위치 점 표시 var dot = new Ellipse { Width = 3, Height = 3, Fill = text.IsInTable ? Brushes.Blue : Brushes.Gray }; Canvas.SetLeft(dot, point.X - 1.5); Canvas.SetTop(dot, point.Y - 1.5); cnvVisualization.Children.Add(dot); } } private void DrawIntersections() { if (_currentData == null) return; foreach (var intersection in _currentData.IntersectionPoints) { var point = TransformPoint(intersection.X, intersection.Y); // 교차점 원 표시 var circle = new Ellipse { Width = 8, Height = 8, Fill = GetIntersectionColor(intersection), Stroke = Brushes.Black, StrokeThickness = 1 }; Canvas.SetLeft(circle, point.X - 4); Canvas.SetTop(circle, point.Y - 4); cnvVisualization.Children.Add(circle); // 교차점 타입 숫자 표시 (우측에) var numberLabel = new TextBlock { Text = intersection.DirectionBits.ToString(), FontSize = 12, FontWeight = FontWeights.Bold, Foreground = Brushes.Black, Background = Brushes.Yellow, Padding = new Thickness(2) }; Canvas.SetLeft(numberLabel, point.X + 8); // 교차점 우측에 표시 Canvas.SetTop(numberLabel, point.Y - 6); // 약간 위쪽으로 조정 cnvVisualization.Children.Add(numberLabel); } } private void DrawDiagonals() { if (_currentData == null) return; foreach (var diagonal in _currentData.DiagonalLines) { var startPoint = TransformPoint(diagonal.StartX, diagonal.StartY); var endPoint = TransformPoint(diagonal.EndX, diagonal.EndY); // 대각선 표시 var line = new Line { X1 = startPoint.X, Y1 = startPoint.Y, X2 = endPoint.X, Y2 = endPoint.Y, Stroke = Brushes.Green, StrokeThickness = 2, StrokeDashArray = new DoubleCollection { 5, 3 } // 점선으로 표시 }; cnvVisualization.Children.Add(line); // 대각선 중앙에 라벨 표시 if (!string.IsNullOrEmpty(diagonal.Label)) { var centerX = (startPoint.X + endPoint.X) / 2; var centerY = (startPoint.Y + endPoint.Y) / 2; var label = new TextBlock { Text = diagonal.Label, FontSize = 9, FontWeight = FontWeights.Bold, Foreground = Brushes.Green, Background = Brushes.White }; Canvas.SetLeft(label, centerX - 20); // 중앙에서 약간 왼쪽으로 Canvas.SetTop(label, centerY - 10); // 중앙에서 약간 위쪽으로 cnvVisualization.Children.Add(label); } } } private System.Windows.Media.Brush GetIntersectionColor(IntersectionInfo intersection) { if (intersection.IsTopLeft && intersection.IsBottomRight) return Brushes.Purple; // 둘 다 가능 else if (intersection.IsTopLeft) return Brushes.Green; // topLeft 후보 else if (intersection.IsBottomRight) return Brushes.Blue; // bottomRight 후보 else return Brushes.Red; // 기타 } private void RefreshVisualization(object sender, RoutedEventArgs e) { RefreshVisualization(); } private void BtnZoomFit_Click(object sender, RoutedEventArgs e) { svViewer.Reset(); } private void CnvVisualization_MouseMove(object sender, MouseEventArgs e) { var pos = e.GetPosition(cnvVisualization); txtMousePos.Text = $"마우스: ({pos.X:F0}, {pos.Y:F0})"; } } }