raycasting note content box detect
This commit is contained in:
@@ -506,6 +506,59 @@ namespace DwgExtractorManual.Models
|
||||
return noteTextIds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 Line과 Polyline 엔터티를 통합된 Line 세그먼트 리스트로 변환합니다.
|
||||
/// Polyline은 구성 Line 세그먼트로 분해됩니다.
|
||||
/// </summary>
|
||||
private List<Line> GetAllLineSegments(Transaction tran, List<ObjectId> polylineIds, List<ObjectId> lineIds)
|
||||
{
|
||||
var allLineSegments = new List<Line>();
|
||||
|
||||
// Polylines를 Line 세그먼트로 분해하여 추가
|
||||
foreach (var polylineId in polylineIds)
|
||||
{
|
||||
using (var polyline = tran.GetObject(polylineId, OpenMode.ForRead) as Polyline)
|
||||
{
|
||||
if (polyline == null) continue;
|
||||
var explodedEntities = new DBObjectCollection();
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allLineSegments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Note 텍스트 아래에 있는 콘텐츠 박스를 찾습니다.
|
||||
/// Note의 정렬점을 기준으로 안정적인 검색 라인을 사용합니다.
|
||||
@@ -513,83 +566,84 @@ 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 allLineSegments = new List<Line>();
|
||||
var allLineSegments = GetAllLineSegments(tran, polylineIds, lineIds);
|
||||
try
|
||||
{
|
||||
var notePos = noteText.Position;
|
||||
var noteHeight = noteText.Height;
|
||||
|
||||
// Polylines를 Line 세그먼트로 분해하여 추가
|
||||
foreach (var polylineId in polylineIds)
|
||||
{
|
||||
using (var polyline = tran.GetObject(polylineId, OpenMode.ForRead) as Polyline)
|
||||
{
|
||||
if (polyline == null) continue;
|
||||
var explodedEntities = new DBObjectCollection();
|
||||
try
|
||||
{
|
||||
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;
|
||||
// Note의 X 좌표 결정 (정렬 방식에 따라)
|
||||
double noteX;
|
||||
if (noteText.HorizontalMode == TextHorizontalMode.TextLeft)
|
||||
{
|
||||
stableX = noteText.Position.X;
|
||||
noteX = noteText.Position.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
stableX = noteText.AlignmentPoint.X;
|
||||
noteX = 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})");
|
||||
Debug.WriteLine($"[DEBUG] 수직 Ray-Casting 시작: NOTE '{noteText.TextString}' at ({noteX:F2}, {notePos.Y:F2})");
|
||||
|
||||
var intersectingLines = FindIntersectingLineSegments(allLineSegments, searchLineStart, searchLineEnd);
|
||||
foreach (var startLine in intersectingLines)
|
||||
// 수직 레이 캐스팅을 위한 하향 스캔
|
||||
var horizontalLines = new List<(Line line, double y)>();
|
||||
|
||||
// 모든 라인 세그먼트를 검사하여 수직 레이와 교차하는 수평선들을 찾음
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
var rectangle = TraceRectangleFromLineSegments(allLineSegments, startLine, notePos, noteHeight, usedBoxes);
|
||||
if (rectangle.HasValue)
|
||||
// 수평선인지 확인 (Y 좌표가 거의 같음)
|
||||
if (Math.Abs(line.StartPoint.Y - line.EndPoint.Y) < noteHeight * 0.1)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 교차하는 Line/Polyline 조합 사각형 발견: {rectangle.Value.minPoint} to {rectangle.Value.maxPoint}");
|
||||
return rectangle;
|
||||
double lineY = (line.StartPoint.Y + line.EndPoint.Y) / 2;
|
||||
double lineMinX = Math.Min(line.StartPoint.X, line.EndPoint.X);
|
||||
double lineMaxX = Math.Max(line.StartPoint.X, line.EndPoint.X);
|
||||
|
||||
// 수직 레이가 이 수평선과 교차하는지 확인
|
||||
if (noteX >= lineMinX && noteX <= lineMaxX && lineY < notePos.Y)
|
||||
{
|
||||
horizontalLines.Add((line, lineY));
|
||||
Debug.WriteLine($"[DEBUG] 수평선 발견: Y={lineY:F2}, X범위=({lineMinX:F2}~{lineMaxX:F2})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Y 좌표로 정렬 (위에서 아래로)
|
||||
var sortedHorizontalLines = horizontalLines
|
||||
.OrderByDescending(hl => hl.y)
|
||||
.ToList();
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 정렬된 수평선 개수: {sortedHorizontalLines.Count}");
|
||||
|
||||
// 두 번째 수평선을 콘텐츠 박스의 상단으로 식별 (첫 번째는 NOTE 자체의 경계로 가정)
|
||||
if (sortedHorizontalLines.Count >= 2)
|
||||
{
|
||||
var topLine = sortedHorizontalLines[1].line;
|
||||
Debug.WriteLine($"[DEBUG] 콘텐츠 박스 상단선 선택 (2번째): Y={sortedHorizontalLines[1].y:F2}");
|
||||
|
||||
// 이 상단선으로부터 박스를 추적
|
||||
var rectangle = TraceBoxFromTopLine(allLineSegments, topLine, notePos, noteHeight, usedBoxes);
|
||||
if (rectangle.HasValue)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] Ray-Casting으로 박스 발견 (2번째 선): {rectangle.Value.minPoint} to {rectangle.Value.maxPoint}");
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
// 2번째 선으로 실패시 3번째 선 시도 (shadow effect polyline 고려)
|
||||
if (sortedHorizontalLines.Count >= 3)
|
||||
{
|
||||
var fallbackTopLine = sortedHorizontalLines[2].line;
|
||||
Debug.WriteLine($"[DEBUG] 대안 콘텐츠 박스 상단선 선택 (3번째): Y={sortedHorizontalLines[2].y:F2}");
|
||||
|
||||
var fallbackRectangle = TraceBoxFromTopLine(allLineSegments, fallbackTopLine, notePos, noteHeight, usedBoxes);
|
||||
if (fallbackRectangle.HasValue)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] Ray-Casting으로 박스 발견 (3번째 선): {fallbackRectangle.Value.minPoint} to {fallbackRectangle.Value.maxPoint}");
|
||||
return fallbackRectangle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"[DEBUG] Ray-Casting으로 적절한 박스를 찾지 못함");
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
@@ -602,133 +656,158 @@ namespace DwgExtractorManual.Models
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
/// <summary>
|
||||
/// 식별된 상단선으로부터 지능적으로 박스의 나머지 세 변을 추적합니다.
|
||||
/// 작은 간격에 대해 관용적입니다.
|
||||
/// </summary>
|
||||
private (Point3d minPoint, Point3d maxPoint)? TraceBoxFromTopLine(
|
||||
List<Line> allLineSegments, Line topLine, 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>();
|
||||
double tolerance = noteHeight * 0.5; // 간격 허용 오차
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 박스 추적 시작: 상단선 ({topLine.StartPoint.X:F2},{topLine.StartPoint.Y:F2}) to ({topLine.EndPoint.X:F2},{topLine.EndPoint.Y:F2})");
|
||||
|
||||
var currentPoint = startLine.StartPoint;
|
||||
var nextPoint = startLine.EndPoint;
|
||||
rectanglePoints.Add(currentPoint);
|
||||
rectanglePoints.Add(nextPoint);
|
||||
visitedLines.Add(startLine);
|
||||
// 상단선의 끝점들
|
||||
var topLeft = new Point3d(Math.Min(topLine.StartPoint.X, topLine.EndPoint.X), topLine.StartPoint.Y, 0);
|
||||
var topRight = new Point3d(Math.Max(topLine.StartPoint.X, topLine.EndPoint.X), topLine.StartPoint.Y, 0);
|
||||
|
||||
for (int step = 0; step < 5; step++)
|
||||
// 왼쪽 수직선 찾기
|
||||
Line leftLine = null;
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
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)
|
||||
if (IsVerticalLine(line, noteHeight) &&
|
||||
IsLineConnectedToPoint(line, topLeft, tolerance))
|
||||
{
|
||||
leftLine = line;
|
||||
Debug.WriteLine($"[DEBUG] 왼쪽 수직선 발견: ({line.StartPoint.X:F2},{line.StartPoint.Y:F2}) to ({line.EndPoint.X:F2},{line.EndPoint.Y:F2})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rectanglePoints.Count >= 4)
|
||||
// 오른쪽 수직선 찾기
|
||||
Line rightLine = null;
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
var bounds = CalculateBounds(rectanglePoints);
|
||||
if (bounds.HasValue && IsValidNoteBox(bounds.Value, notePos, noteHeight) && !usedBoxes.Contains(bounds.Value))
|
||||
if (IsVerticalLine(line, noteHeight) &&
|
||||
IsLineConnectedToPoint(line, topRight, tolerance))
|
||||
{
|
||||
return bounds;
|
||||
rightLine = line;
|
||||
Debug.WriteLine($"[DEBUG] 오른쪽 수직선 발견: ({line.StartPoint.X:F2},{line.StartPoint.Y:F2}) to ({line.EndPoint.X:F2},{line.EndPoint.Y:F2})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leftLine == null || rightLine == null)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 수직선을 찾을 수 없음: left={leftLine != null}, right={rightLine != null}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 하단 끝점들 계산
|
||||
var bottomLeft = GetFarEndPoint(leftLine, topLeft);
|
||||
var bottomRight = GetFarEndPoint(rightLine, topRight);
|
||||
|
||||
// 하단선 찾기
|
||||
Line bottomLine = null;
|
||||
double expectedBottomY = Math.Min(bottomLeft.Y, bottomRight.Y);
|
||||
|
||||
foreach (var line in allLineSegments)
|
||||
{
|
||||
if (IsHorizontalLine(line, noteHeight))
|
||||
{
|
||||
double lineY = (line.StartPoint.Y + line.EndPoint.Y) / 2;
|
||||
if (Math.Abs(lineY - expectedBottomY) < tolerance)
|
||||
{
|
||||
// 하단선이 왼쪽과 오른쪽 끝점을 연결하는지 확인
|
||||
var lineLeft = new Point3d(Math.Min(line.StartPoint.X, line.EndPoint.X), lineY, 0);
|
||||
var lineRight = new Point3d(Math.Max(line.StartPoint.X, line.EndPoint.X), lineY, 0);
|
||||
|
||||
if (Math.Abs(lineLeft.X - bottomLeft.X) < tolerance &&
|
||||
Math.Abs(lineRight.X - bottomRight.X) < tolerance)
|
||||
{
|
||||
bottomLine = line;
|
||||
Debug.WriteLine($"[DEBUG] 하단선 발견: ({line.StartPoint.X:F2},{line.StartPoint.Y:F2}) to ({line.EndPoint.X:F2},{line.EndPoint.Y:F2})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bottomLine == null)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 하단선을 찾을 수 없음");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 최종 박스 경계 계산
|
||||
var minPoint = new Point3d(
|
||||
Math.Min(topLeft.X, bottomLeft.X),
|
||||
Math.Min(topLeft.Y, bottomLeft.Y),
|
||||
0
|
||||
);
|
||||
var maxPoint = new Point3d(
|
||||
Math.Max(topRight.X, bottomRight.X),
|
||||
Math.Max(topLeft.Y, bottomLeft.Y),
|
||||
0
|
||||
);
|
||||
|
||||
var result = (minPoint, maxPoint);
|
||||
|
||||
// 유효성 검사
|
||||
if (IsValidNoteBox(result, notePos, noteHeight) && !usedBoxes.Contains(result))
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 유효한 박스 추적 완료: ({minPoint.X:F2},{minPoint.Y:F2}) to ({maxPoint.X:F2},{maxPoint.Y:F2})");
|
||||
return result;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"[DEBUG] 추적된 박스가 유효하지 않음");
|
||||
return null;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[DEBUG] 통합 사각형 추적 중 오류: {ex.Message}");
|
||||
Debug.WriteLine($"[DEBUG] 박스 추적 중 오류: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private (Line line, string connectionType)? FindNextConnectedLineSegment(List<Line> allLineSegments, Point3d currentPoint, HashSet<Line> visitedLines, double tolerance)
|
||||
/// <summary>
|
||||
/// 선분이 수직선인지 확인합니다.
|
||||
/// </summary>
|
||||
private bool IsVerticalLine(Line line, double noteHeight)
|
||||
{
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch != null)
|
||||
{
|
||||
return (bestMatch, bestConnectionType);
|
||||
}
|
||||
|
||||
return null;
|
||||
return Math.Abs(line.StartPoint.X - line.EndPoint.X) < noteHeight * 0.1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 수평선이 Polyline과 교차하는지 확인합니다.
|
||||
/// 선분이 수평선인지 확인합니다.
|
||||
/// </summary>
|
||||
private bool DoesLineIntersectPolyline(Point3d lineStart, Point3d lineEnd, Polyline polyline)
|
||||
private bool IsHorizontalLine(Line line, double noteHeight)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < polyline.NumberOfVertices; i++)
|
||||
{
|
||||
int nextIndex = (i + 1) % polyline.NumberOfVertices;
|
||||
var segStart = polyline.GetPoint3dAt(i);
|
||||
var segEnd = polyline.GetPoint3dAt(nextIndex);
|
||||
|
||||
// 수평선과 폴리라인 세그먼트의 교차점 확인
|
||||
if (DoLinesIntersect(lineStart, lineEnd, segStart, segEnd))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Math.Abs(line.StartPoint.Y - line.EndPoint.Y) < noteHeight * 0.1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선분이 지정된 점에 연결되어 있는지 확인합니다.
|
||||
/// </summary>
|
||||
private bool IsLineConnectedToPoint(Line line, Point3d point, double tolerance)
|
||||
{
|
||||
return point.DistanceTo(line.StartPoint) < tolerance || point.DistanceTo(line.EndPoint) < tolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선분에서 지정된 점으로부터 가장 먼 끝점을 반환합니다.
|
||||
/// </summary>
|
||||
private Point3d GetFarEndPoint(Line line, Point3d referencePoint)
|
||||
{
|
||||
double distToStart = referencePoint.DistanceTo(line.StartPoint);
|
||||
double distToEnd = referencePoint.DistanceTo(line.EndPoint);
|
||||
|
||||
return distToStart > distToEnd ? line.StartPoint : line.EndPoint;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 두 선분이 교차하는지 확인합니다.
|
||||
/// </summary>
|
||||
@@ -755,33 +834,6 @@ namespace DwgExtractorManual.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polyline의 경계 상자를 계산합니다.
|
||||
/// </summary>
|
||||
private (Point3d minPoint, Point3d maxPoint)? GetPolylineBounds(Polyline polyline)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (polyline.NumberOfVertices < 3) return null;
|
||||
|
||||
var vertices = new List<Point3d>();
|
||||
for (int i = 0; i < polyline.NumberOfVertices; i++)
|
||||
{
|
||||
vertices.Add(polyline.GetPoint3dAt(i));
|
||||
}
|
||||
|
||||
double minX = vertices.Min(v => v.X);
|
||||
double maxX = vertices.Max(v => v.X);
|
||||
double minY = vertices.Min(v => v.Y);
|
||||
double maxY = vertices.Max(v => v.Y);
|
||||
|
||||
return (new Point3d(minX, minY, 0), new Point3d(maxX, maxY, 0));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 박스가 유효한 Note 박스인지 확인합니다.
|
||||
|
||||
Reference in New Issue
Block a user