Files
OpenNest/OpenNest.IO/TitleBlockDetector.cs
T

131 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using ACadSharp;
using OpenNest.Geometry;
namespace OpenNest.IO
{
public static class TitleBlockDetector
{
private static readonly HashSet<string> TitleBlockLayerNames = new(StringComparer.OrdinalIgnoreCase)
{
"TITLE", "TITLEBLOCK", "TITLE_BLOCK", "BORDER", "FRAME",
"TB", "INFO", "SHEET", "ANNOTATION"
};
public static HashSet<Guid> Detect(List<Entity> entities, CadDocument document)
{
var flagged = new HashSet<Guid>();
DetectByLayerName(entities, flagged);
DetectBorder(entities, flagged);
return flagged;
}
private static void DetectByLayerName(List<Entity> entities, HashSet<Guid> flagged)
{
foreach (var entity in entities)
{
if (entity.Layer?.Name != null && TitleBlockLayerNames.Contains(entity.Layer.Name))
flagged.Add(entity.Id);
}
}
private static void DetectBorder(List<Entity> entities, HashSet<Guid> flagged)
{
var lines = entities.OfType<Line>().Where(l => !flagged.Contains(l.Id)).ToList();
if (lines.Count < 4) return;
var bounds = entities.GetBoundingBox();
if (bounds == null || bounds.Area() < OpenNest.Math.Tolerance.Epsilon) return;
var borderCount = 0;
foreach (var line in lines)
{
if (IsBorderLine(line, bounds))
{
flagged.Add(line.Id);
borderCount++;
}
}
if (borderCount >= 2)
DetectZoneMarkers(lines, bounds, flagged);
}
private static bool IsBorderLine(Line line, Box bounds)
{
var dx = line.EndPoint.X - line.StartPoint.X;
var dy = line.EndPoint.Y - line.StartPoint.Y;
var length = System.Math.Sqrt(dx * dx + dy * dy);
var angleRad = System.Math.Atan2(System.Math.Abs(dy), System.Math.Abs(dx));
var angularTolerance = OpenNest.Math.Angle.ToRadians(2.0);
var positionTolerance = System.Math.Max(bounds.Length, bounds.Width) * 0.01;
var isHorizontal = angleRad < angularTolerance;
var isVertical = System.Math.Abs(angleRad - System.Math.PI / 2) < angularTolerance;
if (!isHorizontal && !isVertical) return false;
var minSpan = isHorizontal ? bounds.Length * 0.8 : bounds.Width * 0.8;
if (length < minSpan) return false;
if (isHorizontal)
{
var midY = (line.StartPoint.Y + line.EndPoint.Y) / 2;
return System.Math.Abs(midY - bounds.Bottom) < positionTolerance
|| System.Math.Abs(midY - bounds.Top) < positionTolerance;
}
else
{
var midX = (line.StartPoint.X + line.EndPoint.X) / 2;
return System.Math.Abs(midX - bounds.Left) < positionTolerance
|| System.Math.Abs(midX - bounds.Right) < positionTolerance;
}
}
private static void DetectZoneMarkers(List<Line> lines, Box bounds, HashSet<Guid> flagged)
{
var positionTolerance = System.Math.Max(bounds.Length, bounds.Width) * 0.01;
var maxTickLength = System.Math.Max(bounds.Length, bounds.Width) * 0.05;
var angularTolerance = OpenNest.Math.Angle.ToRadians(2.0);
foreach (var line in lines)
{
if (flagged.Contains(line.Id)) continue;
var dx = line.EndPoint.X - line.StartPoint.X;
var dy = line.EndPoint.Y - line.StartPoint.Y;
var length = System.Math.Sqrt(dx * dx + dy * dy);
if (length > maxTickLength || length < OpenNest.Math.Tolerance.Epsilon) continue;
var angleRad = System.Math.Atan2(System.Math.Abs(dy), System.Math.Abs(dx));
var isVertical = System.Math.Abs(angleRad - System.Math.PI / 2) < angularTolerance;
var isHorizontal = angleRad < angularTolerance;
if (!isVertical && !isHorizontal) continue;
var touchesEdge = false;
if (isVertical)
{
var minY = System.Math.Min(line.StartPoint.Y, line.EndPoint.Y);
var maxY = System.Math.Max(line.StartPoint.Y, line.EndPoint.Y);
touchesEdge = System.Math.Abs(minY - bounds.Bottom) < positionTolerance
|| System.Math.Abs(maxY - bounds.Top) < positionTolerance;
}
else if (isHorizontal)
{
var minX = System.Math.Min(line.StartPoint.X, line.EndPoint.X);
var maxX = System.Math.Max(line.StartPoint.X, line.EndPoint.X);
touchesEdge = System.Math.Abs(minX - bounds.Left) < positionTolerance
|| System.Math.Abs(maxX - bounds.Right) < positionTolerance;
}
if (touchesEdge)
flagged.Add(line.Id);
}
}
}
}