Compare commits
2 Commits
0278688b28
...
107eab90fa
| Author | SHA1 | Date | |
|---|---|---|---|
| 107eab90fa | |||
| 9c76b624bf |
@@ -513,56 +513,189 @@ namespace DwgExtractorManual.Models
|
||||
private (Point3d minPoint, Point3d maxPoint)? FindNoteBox(
|
||||
Transaction tran, DBText noteText, List<ObjectId> polylineIds, List<ObjectId> lineIds, HashSet<(Point3d minPoint, Point3d maxPoint)> usedBoxes)
|
||||
{
|
||||
var notePos = noteText.Position;
|
||||
var noteHeight = noteText.Height;
|
||||
|
||||
double stableX;
|
||||
if (noteText.HorizontalMode == TextHorizontalMode.TextLeft)
|
||||
var allLineSegments = new List<Line>();
|
||||
try
|
||||
{
|
||||
stableX = noteText.Position.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
stableX = noteText.AlignmentPoint.X;
|
||||
}
|
||||
var notePos = noteText.Position;
|
||||
var noteHeight = noteText.Height;
|
||||
|
||||
// 검색 라인 정의: 안정적인 X좌표를 기준으로 하되, 약간의 오차를 허용하는 너비
|
||||
double searchY = notePos.Y - (noteHeight * 2);
|
||||
double searchWidth = noteHeight * 15;
|
||||
var searchLineStart = new Point3d(stableX - noteHeight * 2, searchY, 0);
|
||||
var searchLineEnd = new Point3d(stableX + searchWidth, searchY, 0);
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 안정화된 검색 라인: ({searchLineStart.X:F2}, {searchLineStart.Y:F2}) to ({searchLineEnd.X:F2}, {searchLineEnd.Y:F2}) for NOTE at ({notePos.X:F2}, {notePos.Y:F2}) with stableX={stableX:F2}");
|
||||
|
||||
// 1. Polyline과 교차 확인
|
||||
foreach (var polylineId in polylineIds)
|
||||
{
|
||||
using (var polyline = tran.GetObject(polylineId, OpenMode.ForRead) as Polyline)
|
||||
// Polylines를 Line 세그먼트로 분해하여 추가
|
||||
foreach (var polylineId in polylineIds)
|
||||
{
|
||||
if (polyline == null) continue;
|
||||
|
||||
if (DoesLineIntersectPolyline(searchLineStart, searchLineEnd, polyline))
|
||||
using (var polyline = tran.GetObject(polylineId, OpenMode.ForRead) as Polyline)
|
||||
{
|
||||
var box = GetPolylineBounds(polyline);
|
||||
if (box.HasValue && IsValidNoteBox(box.Value, notePos, noteHeight) && !usedBoxes.Contains(box.Value))
|
||||
if (polyline == null) continue;
|
||||
var explodedEntities = new DBObjectCollection();
|
||||
try
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 교차하는 Polyline 박스 발견: {box.Value.minPoint} to {box.Value.maxPoint}");
|
||||
return box;
|
||||
polyline.Explode(explodedEntities);
|
||||
foreach (DBObject obj in explodedEntities)
|
||||
{
|
||||
if (obj is Line line)
|
||||
{
|
||||
allLineSegments.Add(line.Clone() as Line);
|
||||
}
|
||||
obj.Dispose();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] Polyline 분해 중 오류: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
explodedEntities.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 기존 Line들을 추가
|
||||
foreach (var lineId in lineIds)
|
||||
{
|
||||
using (var line = tran.GetObject(lineId, OpenMode.ForRead) as Line)
|
||||
{
|
||||
if (line != null)
|
||||
{
|
||||
allLineSegments.Add(line.Clone() as Line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double stableX;
|
||||
if (noteText.HorizontalMode == TextHorizontalMode.TextLeft)
|
||||
{
|
||||
stableX = noteText.Position.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
stableX = noteText.AlignmentPoint.X;
|
||||
}
|
||||
|
||||
double searchY = notePos.Y - (noteHeight * 2);
|
||||
double searchWidth = noteHeight * 15;
|
||||
var searchLineStart = new Point3d(stableX - noteHeight * 6, searchY, 0); // 왼쪽으로 확장 (중간값)
|
||||
var searchLineEnd = new Point3d(stableX + searchWidth, searchY, 0);
|
||||
var searchLineEnd = new Point3d(stableX + searchWidth, searchY, 0);
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 확장된 통합 검색 라인: ({searchLineStart.X:F2}, {searchLineStart.Y:F2}) to ({searchLineEnd.X:F2}, {searchLineEnd.Y:F2}) for NOTE at ({notePos.X:F2}, {notePos.Y:F2})");
|
||||
|
||||
var intersectingLines = FindIntersectingLineSegments(allLineSegments, searchLineStart, searchLineEnd);
|
||||
foreach (var startLine in intersectingLines)
|
||||
{
|
||||
var rectangle = TraceRectangleFromLineSegments(allLineSegments, startLine, notePos, noteHeight, usedBoxes);
|
||||
if (rectangle.HasValue)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 교차하는 Line/Polyline 조합 사각형 발견: {rectangle.Value.minPoint} to {rectangle.Value.maxPoint}");
|
||||
return rectangle;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 생성된 모든 Line 객체들을 Dispose
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
line.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Line> FindIntersectingLineSegments(List<Line> allLineSegments, Point3d searchLineStart, Point3d searchLineEnd)
|
||||
{
|
||||
var intersectingLines = new List<Line>();
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
if (DoLinesIntersect(searchLineStart, searchLineEnd, line.StartPoint, line.EndPoint))
|
||||
{
|
||||
intersectingLines.Add(line);
|
||||
}
|
||||
}
|
||||
return intersectingLines;
|
||||
}
|
||||
|
||||
private (Point3d minPoint, Point3d maxPoint)? TraceRectangleFromLineSegments(List<Line> allLineSegments, Line startLine, Point3d notePos, double noteHeight, HashSet<(Point3d minPoint, Point3d maxPoint)> usedBoxes)
|
||||
{
|
||||
try
|
||||
{
|
||||
double tolerance = noteHeight * 2.0;
|
||||
var visitedLines = new HashSet<Line>();
|
||||
var rectanglePoints = new List<Point3d>();
|
||||
|
||||
var currentPoint = startLine.StartPoint;
|
||||
var nextPoint = startLine.EndPoint;
|
||||
rectanglePoints.Add(currentPoint);
|
||||
rectanglePoints.Add(nextPoint);
|
||||
visitedLines.Add(startLine);
|
||||
|
||||
for (int step = 0; step < 5; step++)
|
||||
{
|
||||
var findResult = FindNextConnectedLineSegment(allLineSegments, nextPoint, visitedLines, tolerance);
|
||||
if (findResult == null) break;
|
||||
|
||||
var nextLine = findResult.Value.line;
|
||||
var connectionType = findResult.Value.connectionType;
|
||||
|
||||
Point3d newNextPoint = (connectionType == "Start") ? nextLine.EndPoint : nextLine.StartPoint;
|
||||
|
||||
rectanglePoints.Add(newNextPoint);
|
||||
visitedLines.Add(nextLine);
|
||||
nextPoint = newNextPoint;
|
||||
|
||||
if (nextPoint.DistanceTo(currentPoint) < tolerance)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rectanglePoints.Count >= 4)
|
||||
{
|
||||
var bounds = CalculateBounds(rectanglePoints);
|
||||
if (bounds.HasValue && IsValidNoteBox(bounds.Value, notePos, noteHeight) && !usedBoxes.Contains(bounds.Value))
|
||||
{
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 통합 사각형 추적 중 오류: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private (Line line, string connectionType)? FindNextConnectedLineSegment(List<Line> allLineSegments, Point3d currentPoint, HashSet<Line> visitedLines, double tolerance)
|
||||
{
|
||||
Line bestMatch = null;
|
||||
string bestConnectionType = null;
|
||||
double minDistance = tolerance;
|
||||
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
if (visitedLines.Contains(line)) continue;
|
||||
|
||||
double distToStart = currentPoint.DistanceTo(line.StartPoint);
|
||||
if (distToStart < minDistance)
|
||||
{
|
||||
minDistance = distToStart;
|
||||
bestMatch = line;
|
||||
bestConnectionType = "Start";
|
||||
}
|
||||
|
||||
double distToEnd = currentPoint.DistanceTo(line.EndPoint);
|
||||
if (distToEnd < minDistance)
|
||||
{
|
||||
minDistance = distToEnd;
|
||||
bestMatch = line;
|
||||
bestConnectionType = "End";
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Line들과 교차하여 사각형 찾기
|
||||
var intersectingLines = FindIntersectingLines(tran, lineIds, searchLineStart, searchLineEnd);
|
||||
foreach (var startLineId in intersectingLines)
|
||||
if (bestMatch != null)
|
||||
{
|
||||
var rectangle = TraceRectangleFromLine(tran, lineIds, startLineId, notePos, noteHeight);
|
||||
if (rectangle.HasValue && !usedBoxes.Contains(rectangle.Value))
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 교차하는 Line 사각형 발견: {rectangle.Value.minPoint} to {rectangle.Value.maxPoint}");
|
||||
return rectangle;
|
||||
}
|
||||
return (bestMatch, bestConnectionType);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -682,150 +815,7 @@ namespace DwgExtractorManual.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 수평선과 교차하는 Line들을 찾습니다.
|
||||
/// </summary>
|
||||
private List<ObjectId> FindIntersectingLines(Transaction tran, List<ObjectId> lineIds, Point3d searchLineStart, Point3d searchLineEnd)
|
||||
{
|
||||
var intersectingLines = new List<ObjectId>();
|
||||
|
||||
foreach (var lineId in lineIds)
|
||||
{
|
||||
using (var line = tran.GetObject(lineId, OpenMode.ForRead) as Line)
|
||||
{
|
||||
if (line == null) continue;
|
||||
|
||||
if (DoLinesIntersect(searchLineStart, searchLineEnd, line.StartPoint, line.EndPoint))
|
||||
{
|
||||
intersectingLines.Add(lineId);
|
||||
Debug.WriteLine($"[DEBUG] 교차 Line 발견: ({line.StartPoint.X}, {line.StartPoint.Y}) to ({line.EndPoint.X}, {line.EndPoint.Y})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return intersectingLines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Line에서 시작하여 반시계방향으로 사각형을 추적합니다. (갭 허용)
|
||||
/// </summary>
|
||||
private (Point3d minPoint, Point3d maxPoint)? TraceRectangleFromLine(Transaction tran, List<ObjectId> lineIds, ObjectId startLineId, Point3d notePos, double noteHeight)
|
||||
{
|
||||
try
|
||||
{
|
||||
double tolerance = noteHeight * 2.0; // 갭 허용 오차를 Note 높이에 비례하여 설정
|
||||
var visitedLines = new HashSet<ObjectId>();
|
||||
var rectanglePoints = new List<Point3d>();
|
||||
|
||||
using (var startLine = tran.GetObject(startLineId, OpenMode.ForRead) as Line)
|
||||
{
|
||||
if (startLine == null) return null;
|
||||
|
||||
var currentPoint = startLine.StartPoint;
|
||||
var nextPoint = startLine.EndPoint;
|
||||
rectanglePoints.Add(currentPoint);
|
||||
rectanglePoints.Add(nextPoint);
|
||||
visitedLines.Add(startLineId);
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 사각형 추적 시작 (갭 허용): ({currentPoint.X:F2}, {currentPoint.Y:F2}) -> ({nextPoint.X:F2}, {nextPoint.Y:F2})");
|
||||
|
||||
for (int step = 0; step < 5; step++) // 최대 5번의 연결 시도
|
||||
{
|
||||
var findResult = FindNextConnectedLine(tran, lineIds, nextPoint, visitedLines, tolerance);
|
||||
if (findResult == null) break;
|
||||
|
||||
var nextLineId = findResult.Value.lineId;
|
||||
var connectionType = findResult.Value.connectionType;
|
||||
|
||||
using (var nextLine = tran.GetObject(nextLineId, OpenMode.ForRead) as Line)
|
||||
{
|
||||
if (nextLine == null) break;
|
||||
|
||||
Point3d newNextPoint;
|
||||
if (connectionType == "Start")
|
||||
{
|
||||
newNextPoint = nextLine.EndPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
newNextPoint = nextLine.StartPoint;
|
||||
}
|
||||
|
||||
rectanglePoints.Add(newNextPoint);
|
||||
visitedLines.Add(nextLineId);
|
||||
nextPoint = newNextPoint;
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 다음 Line 추가 (갭 허용): {nextLine.StartPoint} -> {nextLine.EndPoint}");
|
||||
|
||||
if (nextPoint.DistanceTo(currentPoint) < tolerance)
|
||||
{
|
||||
Debug.WriteLine("[DEBUG] 사각형 완성됨 (갭 허용)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rectanglePoints.Count >= 4)
|
||||
{
|
||||
var bounds = CalculateBounds(rectanglePoints);
|
||||
if (bounds.HasValue && IsValidNoteBox(bounds.Value, notePos, noteHeight))
|
||||
{
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 사각형 추적 중 오류 (갭 허용): {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 점에서 가장 가까운 연결된 다음 Line을 찾습니다. (갭 허용)
|
||||
/// </summary>
|
||||
private (ObjectId lineId, string connectionType)? FindNextConnectedLine(Transaction tran, List<ObjectId> lineIds, Point3d currentPoint, HashSet<ObjectId> visitedLines, double tolerance)
|
||||
{
|
||||
ObjectId? bestMatchId = null;
|
||||
string? bestConnectionType = null;
|
||||
double minDistance = tolerance;
|
||||
|
||||
foreach (var lineId in lineIds)
|
||||
{
|
||||
if (visitedLines.Contains(lineId)) continue;
|
||||
|
||||
using (var line = tran.GetObject(lineId, OpenMode.ForRead) as Line)
|
||||
{
|
||||
if (line == null) continue;
|
||||
|
||||
double distToStart = currentPoint.DistanceTo(line.StartPoint);
|
||||
if (distToStart < minDistance)
|
||||
{
|
||||
minDistance = distToStart;
|
||||
bestMatchId = lineId;
|
||||
bestConnectionType = "Start";
|
||||
}
|
||||
|
||||
double distToEnd = currentPoint.DistanceTo(line.EndPoint);
|
||||
if (distToEnd < minDistance)
|
||||
{
|
||||
minDistance = distToEnd;
|
||||
bestMatchId = lineId;
|
||||
bestConnectionType = "End";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatchId.HasValue)
|
||||
{
|
||||
return (bestMatchId.Value, bestConnectionType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 점들의 경계 상자를 계산합니다.
|
||||
|
||||
@@ -340,6 +340,14 @@ namespace DwgExtractorManual.Models
|
||||
worksheet.Cells[row, 5] = fileName ?? "";
|
||||
worksheet.Cells[row, 6] = noteEntity.Text ?? "";
|
||||
|
||||
// "NOTE" 타입인 경우 행 배경색 변경
|
||||
if (noteEntity.Type == "Note")
|
||||
{
|
||||
Excel.Range noteRowRange = worksheet.Range[worksheet.Cells[row, 1], worksheet.Cells[row, 6]];
|
||||
noteRowRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightYellow);
|
||||
noteRowRange.Font.Bold = true;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"[DEBUG] Excel 기록: Row {row}, Order {noteEntity.SortOrder}, Type {noteEntity.Type}, Pos({noteEntity.X:F1},{noteEntity.Y:F1}), Text: '{noteEntity.Text}'");
|
||||
row++;
|
||||
}
|
||||
|
||||
48
NoteDetectionRefactor.md
Normal file
48
NoteDetectionRefactor.md
Normal file
@@ -0,0 +1,48 @@
|
||||
Project: Refactor the NOTE Content Box Detection Algorithm
|
||||
|
||||
1. High-Level Goal:
|
||||
The primary objective is to replace the current, fragile "horizontal search line" algorithm in
|
||||
Models/DwgDataExtractor.cs with a more robust and accurate method that reliably finds the content box for
|
||||
any "NOTE" text, regardless of its position or the composition of its bounding box.
|
||||
|
||||
2. Core Strategy: "Vertical Ray-Casting"
|
||||
We will implement a new algorithm that emulates how a human would visually locate the content. This
|
||||
involves a "gradual downward scan" (or vertical ray-cast) from the NOTE's position.
|
||||
|
||||
3. Implementation Plan (TODO List):
|
||||
|
||||
* Step 1: Unify All Geometry into Line Segments
|
||||
* Create a single helper method, GetAllLineSegments, that processes all Line and Polyline entities from
|
||||
the drawing.
|
||||
* This method will decompose every Polyline into its constituent Line segments.
|
||||
* It will return a single, unified List<Line> containing every potential boundary segment in the
|
||||
drawing.
|
||||
* Crucially: This method must ensure all temporary Line objects created during the process are properly
|
||||
disposed of to prevent memory leaks.
|
||||
|
||||
* Step 2: Implement the Ray-Casting Logic in `FindNoteBox`
|
||||
* The FindNoteBox method will be completely rewritten.
|
||||
* It will first call GetAllLineSegments to get the unified geometry list.
|
||||
* It will then perform the vertical ray-cast starting from the NOTE's X-coordinate and scanning
|
||||
downwards.
|
||||
* It will find all horizontal lines that intersect the ray and sort them by their Y-coordinate (from
|
||||
top to bottom).
|
||||
* It will identify the second line in this sorted list as the top edge of the content box (the first is
|
||||
assumed to be the NOTE's own bounding box).
|
||||
|
||||
* Step 3: Implement Smart Box Tracing
|
||||
* Create a new helper method, TraceBoxFromTopLine.
|
||||
* This method will take the identified top line segment as its starting point.
|
||||
* It will intelligently trace the remaining three sides of the rectangle by searching the unified list
|
||||
of line segments for the nearest connecting corners.
|
||||
* This tracing logic must be tolerant of small gaps between the endpoints of the lines forming the box.
|
||||
|
||||
* Step 4: Final Cleanup
|
||||
* Once the new ray-casting algorithm is fully implemented and validated, all of the old, obsolete
|
||||
methods related to the previous search-line approach must be deleted to keep the code clean. This
|
||||
includes:
|
||||
* FindIntersectingLineSegments
|
||||
* TraceRectangleFromLineSegments
|
||||
* FindNextConnectedLineSegment
|
||||
* DoesLineIntersectPolyline
|
||||
* GetPolylineBounds
|
||||
Reference in New Issue
Block a user