Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1945270fa7 | |||
| a18b5398de | |||
| 9d1a39aa8f | |||
| cc38934d10 | |||
| 4f849f1c06 | |||
| 4f2a8d29d5 | |||
| 09a5339b51 | |||
| 77ed1a1522 | |||
| 8ac3f5622c | |||
| c3494681a8 |
@@ -16,6 +16,11 @@ namespace OpenNest.IO
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DetectBends { get; set; } = true;
|
public bool DetectBends { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When true, detects and identifies title block entities during import. Default true.
|
||||||
|
/// </summary>
|
||||||
|
public bool DetectTitleBlock { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Override the drawing name. Null = filename without extension.
|
/// Override the drawing name. Null = filename without extension.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -45,5 +45,10 @@ namespace OpenNest.IO
|
|||||||
/// that need access to non-geometry entities (e.g., text annotations).
|
/// that need access to non-geometry entities (e.g., text annotations).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CadDocument Document { get; set; }
|
public CadDocument Document { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GUIDs of entities identified as part of the title block during import.
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<System.Guid> TitleBlockEntityIds { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace OpenNest.IO
|
|||||||
var dxf = Dxf.Import(path);
|
var dxf = Dxf.Import(path);
|
||||||
|
|
||||||
RemoveDuplicateArcs(dxf.Entities);
|
RemoveDuplicateArcs(dxf.Entities);
|
||||||
|
RemoveZeroSweepArcs(dxf.Entities);
|
||||||
|
|
||||||
var bends = new List<Bend>();
|
var bends = new List<Bend>();
|
||||||
if (options.DetectBends && dxf.Document != null)
|
if (options.DetectBends && dxf.Document != null)
|
||||||
@@ -40,6 +41,10 @@ namespace OpenNest.IO
|
|||||||
|
|
||||||
Bend.UpdateEtchEntities(dxf.Entities, bends);
|
Bend.UpdateEtchEntities(dxf.Entities, bends);
|
||||||
|
|
||||||
|
HashSet<System.Guid> titleBlockIds = null;
|
||||||
|
if (options.DetectTitleBlock)
|
||||||
|
titleBlockIds = TitleBlockDetector.Detect(dxf.Entities, dxf.Document);
|
||||||
|
|
||||||
return new CadImportResult
|
return new CadImportResult
|
||||||
{
|
{
|
||||||
Entities = dxf.Entities,
|
Entities = dxf.Entities,
|
||||||
@@ -48,6 +53,7 @@ namespace OpenNest.IO
|
|||||||
SourcePath = path,
|
SourcePath = path,
|
||||||
Name = options.Name ?? Path.GetFileNameWithoutExtension(path),
|
Name = options.Name ?? Path.GetFileNameWithoutExtension(path),
|
||||||
Document = dxf.Document,
|
Document = dxf.Document,
|
||||||
|
TitleBlockEntityIds = titleBlockIds,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,9 +144,25 @@ namespace OpenNest.IO
|
|||||||
.Where(e => !(e.Layer != null && e.Layer.IsVisible && e.IsVisible))
|
.Where(e => !(e.Layer != null && e.Layer.IsVisible && e.IsVisible))
|
||||||
.Select(e => e.Id));
|
.Select(e => e.Id));
|
||||||
|
|
||||||
|
if (result.TitleBlockEntityIds != null)
|
||||||
|
{
|
||||||
|
var sourceIds = new HashSet<System.Guid>(drawing.SourceEntities.Select(e => e.Id));
|
||||||
|
foreach (var id in result.TitleBlockEntityIds)
|
||||||
|
{
|
||||||
|
if (sourceIds.Contains(id))
|
||||||
|
drawing.SuppressedEntityIds.Add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return drawing;
|
return drawing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void RemoveZeroSweepArcs(List<Entity> entities)
|
||||||
|
{
|
||||||
|
entities.RemoveAll(e =>
|
||||||
|
e is Arc arc && arc.StartAngle.IsEqualTo(arc.EndAngle, Tolerance.ChainTolerance));
|
||||||
|
}
|
||||||
|
|
||||||
internal static void RemoveDuplicateArcs(List<Entity> entities)
|
internal static void RemoveDuplicateArcs(List<Entity> entities)
|
||||||
{
|
{
|
||||||
var circles = entities.OfType<Circle>().ToList();
|
var circles = entities.OfType<Circle>().ToList();
|
||||||
|
|||||||
+2
-33
@@ -65,36 +65,6 @@ namespace OpenNest.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Entity> GetGeometry(string path, Func<string, bool> layerFilter)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var reader = new DxfReader(path);
|
|
||||||
var doc = reader.Read();
|
|
||||||
return ConvertEntities(doc, layerFilter);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine(ex.Message);
|
|
||||||
return new List<Entity>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Entity> GetGeometry(Stream stream, Func<string, bool> layerFilter)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var reader = new DxfReader(stream);
|
|
||||||
var doc = reader.Read();
|
|
||||||
return ConvertEntities(doc, layerFilter);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.WriteLine(ex.Message);
|
|
||||||
return new List<Entity>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Export
|
#region Export
|
||||||
@@ -158,17 +128,16 @@ namespace OpenNest.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Entity> ConvertEntities(CadDocument doc, Func<string, bool> layerFilter = null)
|
private static List<Entity> ConvertEntities(CadDocument doc)
|
||||||
{
|
{
|
||||||
var entities = new List<Entity>();
|
var entities = new List<Entity>();
|
||||||
var lines = new List<Line>();
|
var lines = new List<Line>();
|
||||||
var arcs = new List<Arc>();
|
var arcs = new List<Arc>();
|
||||||
var circles = new List<Circle>();
|
var circles = new List<Circle>();
|
||||||
var filter = layerFilter ?? IsNonCutLayer;
|
|
||||||
|
|
||||||
foreach (var entity in doc.Entities)
|
foreach (var entity in doc.Entities)
|
||||||
{
|
{
|
||||||
if (filter(entity.Layer?.Name))
|
if (IsNonCutLayer(entity.Layer?.Name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (entity)
|
switch (entity)
|
||||||
|
|||||||
@@ -0,0 +1,312 @@
|
|||||||
|
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);
|
||||||
|
if (document != null)
|
||||||
|
DetectTitleBlockRegion(entities, document, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DetectTitleBlockRegion(List<Entity> entities, CadDocument document, HashSet<Guid> flagged)
|
||||||
|
{
|
||||||
|
var textPositions = ExtractTextPositions(document);
|
||||||
|
if (textPositions.Count < 3) return;
|
||||||
|
|
||||||
|
var unflagged = entities.Where(e => !flagged.Contains(e.Id)).ToList();
|
||||||
|
if (unflagged.Count == 0) return;
|
||||||
|
|
||||||
|
var bounds = entities.GetBoundingBox();
|
||||||
|
if (bounds == null || bounds.Area() < OpenNest.Math.Tolerance.Epsilon) return;
|
||||||
|
|
||||||
|
var bestRegion = FindBestTitleBlockRegion(bounds, textPositions, unflagged);
|
||||||
|
if (bestRegion == null) return;
|
||||||
|
|
||||||
|
var initiallyInside = unflagged.Where(e => {
|
||||||
|
var c = EntityCenter(e);
|
||||||
|
return c.HasValue && RegionContains(bestRegion, c.Value);
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var expandedBounds = initiallyInside.Count > 0 ? initiallyInside.GetBoundingBox() : null;
|
||||||
|
|
||||||
|
foreach (var entity in unflagged)
|
||||||
|
{
|
||||||
|
var center = EntityCenter(entity);
|
||||||
|
if (!center.HasValue) continue;
|
||||||
|
if (RegionContains(bestRegion, center.Value)
|
||||||
|
|| (expandedBounds != null && RegionContains(expandedBounds, center.Value)))
|
||||||
|
flagged.Add(entity.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Vector> ExtractTextPositions(CadDocument document)
|
||||||
|
{
|
||||||
|
var positions = new List<Vector>();
|
||||||
|
foreach (var entity in document.Entities)
|
||||||
|
{
|
||||||
|
switch (entity)
|
||||||
|
{
|
||||||
|
case ACadSharp.Entities.MText mtext:
|
||||||
|
positions.Add(new Vector(mtext.InsertPoint.X, mtext.InsertPoint.Y));
|
||||||
|
break;
|
||||||
|
case ACadSharp.Entities.TextEntity text:
|
||||||
|
var pt = text.HorizontalAlignment != 0 || text.VerticalAlignment != 0
|
||||||
|
? text.AlignmentPoint : text.InsertPoint;
|
||||||
|
positions.Add(new Vector(pt.X, pt.Y));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Box FindBestTitleBlockRegion(Box bounds, List<Vector> textPositions, List<Entity> entities)
|
||||||
|
{
|
||||||
|
var candidates = GenerateCandidateRegions(bounds);
|
||||||
|
Box bestRegion = null;
|
||||||
|
var bestScore = 0.0;
|
||||||
|
|
||||||
|
var openLines = FindOpenLines(entities);
|
||||||
|
|
||||||
|
foreach (var region in candidates)
|
||||||
|
{
|
||||||
|
var textCount = textPositions.Count(p => RegionContains(region, p));
|
||||||
|
if (textCount < 3) continue;
|
||||||
|
|
||||||
|
var openLineCount = openLines.Count(l => RegionContains(region, l.MidPoint));
|
||||||
|
|
||||||
|
var area = region.Area();
|
||||||
|
if (area < OpenNest.Math.Tolerance.Epsilon) continue;
|
||||||
|
|
||||||
|
var score = (double)textCount + openLineCount * 0.5;
|
||||||
|
|
||||||
|
var regionCenterX = (region.Left + region.Right) / 2;
|
||||||
|
var regionCenterY = (region.Bottom + region.Top) / 2;
|
||||||
|
if (regionCenterX > bounds.Center.X) score *= 1.3;
|
||||||
|
if (regionCenterY < bounds.Center.Y) score *= 1.3;
|
||||||
|
|
||||||
|
if (score > bestScore)
|
||||||
|
{
|
||||||
|
bestScore = score;
|
||||||
|
bestRegion = region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Box> GenerateCandidateRegions(Box bounds)
|
||||||
|
{
|
||||||
|
var regions = new List<Box>();
|
||||||
|
var fractions = new[] { 0.25, 0.333, 0.5 };
|
||||||
|
|
||||||
|
foreach (var fx in fractions)
|
||||||
|
{
|
||||||
|
foreach (var fy in fractions)
|
||||||
|
{
|
||||||
|
var w = bounds.Length * fx;
|
||||||
|
var h = bounds.Width * fy;
|
||||||
|
|
||||||
|
regions.Add(new Box(bounds.Right - w, bounds.Bottom, w, h));
|
||||||
|
regions.Add(new Box(bounds.Left, bounds.Bottom, w, h));
|
||||||
|
regions.Add(new Box(bounds.Right - w, bounds.Top - h, w, h));
|
||||||
|
regions.Add(new Box(bounds.Left, bounds.Top - h, w, h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var fy in fractions)
|
||||||
|
{
|
||||||
|
var h = bounds.Width * fy;
|
||||||
|
regions.Add(new Box(bounds.Left, bounds.Bottom, bounds.Length, h));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var fx in fractions)
|
||||||
|
{
|
||||||
|
var w = bounds.Length * fx;
|
||||||
|
regions.Add(new Box(bounds.Right - w, bounds.Bottom, w, bounds.Width));
|
||||||
|
}
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Line> FindOpenLines(List<Entity> entities)
|
||||||
|
{
|
||||||
|
var endpointUsers = new Dictionary<long, int>();
|
||||||
|
|
||||||
|
foreach (var entity in entities)
|
||||||
|
{
|
||||||
|
foreach (var ep in GetEntityEndpoints(entity))
|
||||||
|
{
|
||||||
|
var key = QuantizePoint(ep);
|
||||||
|
endpointUsers[key] = endpointUsers.GetValueOrDefault(key) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var openLines = new List<Line>();
|
||||||
|
foreach (var line in entities.OfType<Line>())
|
||||||
|
{
|
||||||
|
var startKey = QuantizePoint(line.StartPoint);
|
||||||
|
var endKey = QuantizePoint(line.EndPoint);
|
||||||
|
|
||||||
|
if (endpointUsers.GetValueOrDefault(startKey) <= 1 || endpointUsers.GetValueOrDefault(endKey) <= 1)
|
||||||
|
openLines.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return openLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Vector> GetEntityEndpoints(Entity entity)
|
||||||
|
{
|
||||||
|
return entity switch
|
||||||
|
{
|
||||||
|
Line line => new List<Vector> { line.StartPoint, line.EndPoint },
|
||||||
|
Arc arc => new List<Vector> { arc.StartPoint(), arc.EndPoint() },
|
||||||
|
_ => new List<Vector>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long QuantizePoint(Vector pt)
|
||||||
|
{
|
||||||
|
var qx = (long)(pt.X * 1000);
|
||||||
|
var qy = (long)(pt.Y * 1000);
|
||||||
|
return qx * 100000000L + qy;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector? EntityCenter(Entity entity)
|
||||||
|
{
|
||||||
|
return entity switch
|
||||||
|
{
|
||||||
|
Line line => line.MidPoint,
|
||||||
|
Arc arc => arc.Center,
|
||||||
|
Circle circle => circle.Center,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RegionContains(Box box, Vector pt)
|
||||||
|
{
|
||||||
|
return pt.X >= box.Left && pt.X <= box.Right
|
||||||
|
&& pt.Y >= box.Bottom && pt.Y <= box.Top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -134,5 +134,21 @@ namespace OpenNest.Tests.IO
|
|||||||
Assert.NotNull(drawing.Program);
|
Assert.NotNull(drawing.Program);
|
||||||
Assert.NotNull(drawing.SourceEntities);
|
Assert.NotNull(drawing.SourceEntities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Import_WhenDetectTitleBlockTrue_PopulatesTitleBlockEntityIds()
|
||||||
|
{
|
||||||
|
var result = CadImporter.Import(TestDxf);
|
||||||
|
|
||||||
|
Assert.NotNull(result.TitleBlockEntityIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Import_WhenDetectTitleBlockFalse_TitleBlockEntityIdsIsNull()
|
||||||
|
{
|
||||||
|
var result = CadImporter.Import(TestDxf, new CadImportOptions { DetectTitleBlock = false });
|
||||||
|
|
||||||
|
Assert.Null(result.TitleBlockEntityIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,245 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CSMath;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
using OpenNest.IO;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests.IO
|
||||||
|
{
|
||||||
|
public class TitleBlockDetectorTests
|
||||||
|
{
|
||||||
|
private static Line MakeLine(double x1, double y1, double x2, double y2) =>
|
||||||
|
new Line(x1, y1, x2, y2);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectByLayerName_FlagsTitleLayer()
|
||||||
|
{
|
||||||
|
var line = MakeLine(0, 0, 10, 0);
|
||||||
|
line.Layer = new Layer("TITLE");
|
||||||
|
var entities = new List<Entity> { line };
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Contains(line.Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectByLayerName_CaseInsensitive()
|
||||||
|
{
|
||||||
|
var line = MakeLine(0, 0, 10, 0);
|
||||||
|
line.Layer = new Layer("border");
|
||||||
|
var entities = new List<Entity> { line };
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Contains(line.Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectByLayerName_IgnoresNonMatchingLayers()
|
||||||
|
{
|
||||||
|
var line = MakeLine(0, 0, 10, 0);
|
||||||
|
line.Layer = new Layer("0");
|
||||||
|
var entities = new List<Entity> { line };
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.DoesNotContain(line.Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("TITLE")]
|
||||||
|
[InlineData("TITLEBLOCK")]
|
||||||
|
[InlineData("TITLE_BLOCK")]
|
||||||
|
[InlineData("BORDER")]
|
||||||
|
[InlineData("FRAME")]
|
||||||
|
[InlineData("TB")]
|
||||||
|
[InlineData("INFO")]
|
||||||
|
[InlineData("SHEET")]
|
||||||
|
[InlineData("ANNOTATION")]
|
||||||
|
public void DetectByLayerName_AllKnownNames(string layerName)
|
||||||
|
{
|
||||||
|
var line = MakeLine(0, 0, 10, 0);
|
||||||
|
line.Layer = new Layer(layerName);
|
||||||
|
var entities = new List<Entity> { line };
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Contains(line.Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectBorder_FlagsLinesOnBoundingBoxEdges()
|
||||||
|
{
|
||||||
|
var entities = new List<Entity>
|
||||||
|
{
|
||||||
|
new Line(0, 0, 86, 0) { Layer = new Layer("0") },
|
||||||
|
new Line(86, 0, 86, 134) { Layer = new Layer("0") },
|
||||||
|
new Line(86, 134, 0, 134) { Layer = new Layer("0") },
|
||||||
|
new Line(0, 134, 0, 0) { Layer = new Layer("0") },
|
||||||
|
new Line(30, 40, 50, 90) { Layer = new Layer("0") },
|
||||||
|
new Line(50, 90, 70, 40) { Layer = new Layer("0") },
|
||||||
|
new Line(70, 40, 30, 40) { Layer = new Layer("0") },
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Contains(entities[0].Id, result);
|
||||||
|
Assert.Contains(entities[1].Id, result);
|
||||||
|
Assert.Contains(entities[2].Id, result);
|
||||||
|
Assert.Contains(entities[3].Id, result);
|
||||||
|
Assert.DoesNotContain(entities[4].Id, result);
|
||||||
|
Assert.DoesNotContain(entities[5].Id, result);
|
||||||
|
Assert.DoesNotContain(entities[6].Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectBorder_FlagsZoneMarkerTicks()
|
||||||
|
{
|
||||||
|
var entities = new List<Entity>
|
||||||
|
{
|
||||||
|
new Line(0, 0, 100, 0) { Layer = new Layer("0") },
|
||||||
|
new Line(100, 0, 100, 80) { Layer = new Layer("0") },
|
||||||
|
new Line(100, 80, 0, 80) { Layer = new Layer("0") },
|
||||||
|
new Line(0, 80, 0, 0) { Layer = new Layer("0") },
|
||||||
|
new Line(25, 80, 25, 77) { Layer = new Layer("0") },
|
||||||
|
new Line(50, 80, 50, 77) { Layer = new Layer("0") },
|
||||||
|
new Line(75, 80, 75, 77) { Layer = new Layer("0") },
|
||||||
|
new Line(40, 30, 60, 30) { Layer = new Layer("0") },
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Contains(entities[4].Id, result);
|
||||||
|
Assert.Contains(entities[5].Id, result);
|
||||||
|
Assert.Contains(entities[6].Id, result);
|
||||||
|
Assert.DoesNotContain(entities[7].Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectBorder_IgnoresWhenNoBorderPresent()
|
||||||
|
{
|
||||||
|
var entities = new List<Entity>
|
||||||
|
{
|
||||||
|
new Line(30, 40, 50, 90) { Layer = new Layer("0") },
|
||||||
|
new Line(50, 90, 70, 40) { Layer = new Layer("0") },
|
||||||
|
new Line(70, 40, 30, 40) { Layer = new Layer("0") },
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectBorder_ToleratesSlightRotation()
|
||||||
|
{
|
||||||
|
var angleRad = OpenNest.Math.Angle.ToRadians(0.5);
|
||||||
|
var endY = 86 * System.Math.Sin(angleRad);
|
||||||
|
var entities = new List<Entity>
|
||||||
|
{
|
||||||
|
new Line(0, 0, 86, endY) { Layer = new Layer("0") },
|
||||||
|
new Line(86, endY, 86, 134) { Layer = new Layer("0") },
|
||||||
|
new Line(86, 134, 0, 134) { Layer = new Layer("0") },
|
||||||
|
new Line(0, 134, 0, 0) { Layer = new Layer("0") },
|
||||||
|
new Line(30, 40, 50, 90) { Layer = new Layer("0") },
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Contains(entities[0].Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectTitleBlock_FlagsEntitiesInTextDenseCorner()
|
||||||
|
{
|
||||||
|
var partLine1 = new Line(5, 70, 25, 120) { Layer = new Layer("0") };
|
||||||
|
var partLine2 = new Line(25, 120, 45, 70) { Layer = new Layer("0") };
|
||||||
|
var partLine3 = new Line(45, 70, 5, 70) { Layer = new Layer("0") };
|
||||||
|
|
||||||
|
var tbLines = new List<Entity>();
|
||||||
|
for (var x = 50; x <= 85; x += 5)
|
||||||
|
tbLines.Add(new Line(x, 0, x, 30) { Layer = new Layer("0") });
|
||||||
|
for (var y = 0; y <= 30; y += 5)
|
||||||
|
tbLines.Add(new Line(50, y, 85, y) { Layer = new Layer("0") });
|
||||||
|
|
||||||
|
var entities = new List<Entity> { partLine1, partLine2, partLine3 };
|
||||||
|
entities.AddRange(tbLines);
|
||||||
|
|
||||||
|
var doc = BuildDocWithTexts(
|
||||||
|
(60, 5, "TITLE: Test Part"),
|
||||||
|
(60, 10, "DWG NO: 12345"),
|
||||||
|
(60, 15, "SCALE: 1:1"),
|
||||||
|
(60, 20, "REV: A"),
|
||||||
|
(60, 25, "MATERIAL: STEEL"));
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, doc);
|
||||||
|
|
||||||
|
foreach (var tb in tbLines)
|
||||||
|
Assert.Contains(tb.Id, result);
|
||||||
|
Assert.DoesNotContain(partLine1.Id, result);
|
||||||
|
Assert.DoesNotContain(partLine2.Id, result);
|
||||||
|
Assert.DoesNotContain(partLine3.Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectTitleBlock_NoFalsePositivesWithoutText()
|
||||||
|
{
|
||||||
|
var entities = new List<Entity>
|
||||||
|
{
|
||||||
|
new Line(30, 40, 50, 90) { Layer = new Layer("0") },
|
||||||
|
new Line(50, 90, 70, 40) { Layer = new Layer("0") },
|
||||||
|
new Line(70, 40, 30, 40) { Layer = new Layer("0") },
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, null);
|
||||||
|
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DetectTitleBlock_BottomEdgeStrip()
|
||||||
|
{
|
||||||
|
var partLine = new Line(20, 40, 80, 40) { Layer = new Layer("0") };
|
||||||
|
|
||||||
|
var tbLines = new List<Entity>();
|
||||||
|
for (var x = 0; x <= 100; x += 10)
|
||||||
|
tbLines.Add(new Line(x, 0, x, 20) { Layer = new Layer("0") });
|
||||||
|
for (var y = 0; y <= 20; y += 5)
|
||||||
|
tbLines.Add(new Line(0, y, 100, y) { Layer = new Layer("0") });
|
||||||
|
|
||||||
|
var entities = new List<Entity> { partLine };
|
||||||
|
entities.AddRange(tbLines);
|
||||||
|
|
||||||
|
var doc = BuildDocWithTexts(
|
||||||
|
(10, 5, "TITLE"),
|
||||||
|
(30, 5, "DWG NO"),
|
||||||
|
(50, 5, "SCALE"),
|
||||||
|
(70, 5, "REV"),
|
||||||
|
(90, 5, "DATE"));
|
||||||
|
|
||||||
|
var result = TitleBlockDetector.Detect(entities, doc);
|
||||||
|
|
||||||
|
foreach (var tb in tbLines)
|
||||||
|
Assert.Contains(tb.Id, result);
|
||||||
|
Assert.DoesNotContain(partLine.Id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ACadSharp.CadDocument BuildDocWithTexts(
|
||||||
|
params (double x, double y, string value)[] texts)
|
||||||
|
{
|
||||||
|
var doc = new ACadSharp.CadDocument();
|
||||||
|
foreach (var (x, y, value) in texts)
|
||||||
|
{
|
||||||
|
var mtext = new ACadSharp.Entities.MText
|
||||||
|
{
|
||||||
|
InsertPoint = new XYZ(x, y, 0),
|
||||||
|
Value = value,
|
||||||
|
Height = 2.0
|
||||||
|
};
|
||||||
|
doc.Entities.Add(mtext);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,15 +16,11 @@ namespace OpenNest.Actions
|
|||||||
private CutOffSettings settings;
|
private CutOffSettings settings;
|
||||||
private CutOffAxis lockedAxis = CutOffAxis.Vertical;
|
private CutOffAxis lockedAxis = CutOffAxis.Vertical;
|
||||||
private Dictionary<Part, Entity> perimeterCache;
|
private Dictionary<Part, Entity> perimeterCache;
|
||||||
private readonly Timer debounceTimer;
|
|
||||||
private bool regeneratePending;
|
|
||||||
|
|
||||||
public ActionCutOff(PlateView plateView)
|
public ActionCutOff(PlateView plateView)
|
||||||
: base(plateView)
|
: base(plateView)
|
||||||
{
|
{
|
||||||
settings = plateView.CutOffSettings;
|
settings = plateView.CutOffSettings;
|
||||||
debounceTimer = new Timer { Interval = 16 };
|
|
||||||
debounceTimer.Tick += OnDebounce;
|
|
||||||
ConnectEvents();
|
ConnectEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +36,6 @@ namespace OpenNest.Actions
|
|||||||
|
|
||||||
public override void DisconnectEvents()
|
public override void DisconnectEvents()
|
||||||
{
|
{
|
||||||
debounceTimer.Stop();
|
|
||||||
debounceTimer.Dispose();
|
|
||||||
plateView.MouseMove -= OnMouseMove;
|
plateView.MouseMove -= OnMouseMove;
|
||||||
plateView.MouseDown -= OnMouseDown;
|
plateView.MouseDown -= OnMouseDown;
|
||||||
plateView.KeyDown -= OnKeyDown;
|
plateView.KeyDown -= OnKeyDown;
|
||||||
@@ -58,18 +52,6 @@ namespace OpenNest.Actions
|
|||||||
|
|
||||||
private void OnMouseMove(object sender, MouseEventArgs e)
|
private void OnMouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
regeneratePending = true;
|
|
||||||
debounceTimer.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDebounce(object sender, System.EventArgs e)
|
|
||||||
{
|
|
||||||
debounceTimer.Stop();
|
|
||||||
|
|
||||||
if (!regeneratePending)
|
|
||||||
return;
|
|
||||||
|
|
||||||
regeneratePending = false;
|
|
||||||
var pt = plateView.CurrentPoint;
|
var pt = plateView.CurrentPoint;
|
||||||
previewCutOff = new CutOff(pt, lockedAxis);
|
previewCutOff = new CutOff(pt, lockedAxis);
|
||||||
previewCutOff.Regenerate(plateView.Plate, settings, perimeterCache);
|
previewCutOff.Regenerate(plateView.Plate, settings, perimeterCache);
|
||||||
|
|||||||
@@ -30,13 +30,16 @@ namespace OpenNest.Controls
|
|||||||
public List<Entity> OriginalEntities { get; set; }
|
public List<Entity> OriginalEntities { get; set; }
|
||||||
public bool ShowEntityLabels { get; set; }
|
public bool ShowEntityLabels { get; set; }
|
||||||
public List<CadText> Texts { get; set; } = new List<CadText>();
|
public List<CadText> Texts { get; set; } = new List<CadText>();
|
||||||
|
public HashSet<Guid> TitleBlockEntityIds { get; set; }
|
||||||
|
|
||||||
private readonly Pen gridPen = new Pen(Color.FromArgb(70, 70, 70));
|
private readonly Pen gridPen = new Pen(Color.FromArgb(70, 70, 70));
|
||||||
private readonly Dictionary<int, Pen> penCache = new Dictionary<int, Pen>();
|
private readonly Dictionary<int, Pen> penCache = new Dictionary<int, Pen>();
|
||||||
|
private readonly Dictionary<int, Pen> ghostPenCache = new Dictionary<int, Pen>();
|
||||||
private readonly Font labelFont = new Font("Segoe UI", 7f);
|
private readonly Font labelFont = new Font("Segoe UI", 7f);
|
||||||
private readonly SolidBrush labelBrush = new SolidBrush(Color.FromArgb(220, 255, 255, 200));
|
private readonly SolidBrush labelBrush = new SolidBrush(Color.FromArgb(220, 255, 255, 200));
|
||||||
private readonly SolidBrush labelBackBrush = new SolidBrush(Color.FromArgb(33, 40, 48));
|
private readonly SolidBrush labelBackBrush = new SolidBrush(Color.FromArgb(33, 40, 48));
|
||||||
private readonly SolidBrush textBrush = new SolidBrush(Color.FromArgb(180, 200, 200, 200));
|
private readonly SolidBrush textBrush = new SolidBrush(Color.FromArgb(180, 200, 200, 200));
|
||||||
|
private readonly SolidBrush ghostTextBrush = new SolidBrush(Color.FromArgb(50, 200, 200, 200));
|
||||||
|
|
||||||
public event EventHandler<Line> LinePicked;
|
public event EventHandler<Line> LinePicked;
|
||||||
public event EventHandler PickCancelled;
|
public event EventHandler PickCancelled;
|
||||||
@@ -102,6 +105,13 @@ namespace OpenNest.Controls
|
|||||||
foreach (var entity in Entities)
|
foreach (var entity in Entities)
|
||||||
{
|
{
|
||||||
if (IsEtchLayer(entity.Layer)) continue;
|
if (IsEtchLayer(entity.Layer)) continue;
|
||||||
|
|
||||||
|
if (TitleBlockEntityIds != null && TitleBlockEntityIds.Contains(entity.Id))
|
||||||
|
{
|
||||||
|
DrawGhostEntity(e.Graphics, entity);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var isHighlighted = simplifierHighlightSet != null && simplifierHighlightSet.Contains(entity);
|
var isHighlighted = simplifierHighlightSet != null && simplifierHighlightSet.Contains(entity);
|
||||||
var pen = isHighlighted
|
var pen = isHighlighted
|
||||||
? GetEntityPen(Color.FromArgb(60, entity.Color))
|
? GetEntityPen(Color.FromArgb(60, entity.Color))
|
||||||
@@ -243,11 +253,26 @@ namespace OpenNest.Controls
|
|||||||
return pen;
|
return pen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Pen GetGhostPen(Color color)
|
||||||
|
{
|
||||||
|
var ghostColor = Color.FromArgb(60, color.R, color.G, color.B);
|
||||||
|
var argb = ghostColor.ToArgb();
|
||||||
|
if (!ghostPenCache.TryGetValue(argb, out var pen))
|
||||||
|
{
|
||||||
|
pen = new Pen(ghostColor);
|
||||||
|
ghostPenCache[argb] = pen;
|
||||||
|
}
|
||||||
|
return pen;
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearPenCache()
|
public void ClearPenCache()
|
||||||
{
|
{
|
||||||
foreach (var pen in penCache.Values)
|
foreach (var pen in penCache.Values)
|
||||||
pen.Dispose();
|
pen.Dispose();
|
||||||
penCache.Clear();
|
penCache.Clear();
|
||||||
|
foreach (var pen in ghostPenCache.Values)
|
||||||
|
pen.Dispose();
|
||||||
|
ghostPenCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsEtchLayer(Layer layer) =>
|
private static bool IsEtchLayer(Layer layer) =>
|
||||||
@@ -413,10 +438,28 @@ namespace OpenNest.Controls
|
|||||||
labelBrush.Dispose();
|
labelBrush.Dispose();
|
||||||
labelBackBrush.Dispose();
|
labelBackBrush.Dispose();
|
||||||
textBrush.Dispose();
|
textBrush.Dispose();
|
||||||
|
ghostTextBrush.Dispose();
|
||||||
}
|
}
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawGhostEntity(Graphics g, Entity e)
|
||||||
|
{
|
||||||
|
var pen = GetGhostPen(e.Color);
|
||||||
|
switch (e.Type)
|
||||||
|
{
|
||||||
|
case EntityType.Arc:
|
||||||
|
DrawArc(g, (Arc)e, pen);
|
||||||
|
break;
|
||||||
|
case EntityType.Circle:
|
||||||
|
DrawCircle(g, (Circle)e, pen);
|
||||||
|
break;
|
||||||
|
case EntityType.Line:
|
||||||
|
DrawLine(g, (Line)e, pen);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawEntity(Graphics g, Entity e, Pen pen)
|
private void DrawEntity(Graphics g, Entity e, Pen pen)
|
||||||
{
|
{
|
||||||
if (!e.Layer.IsVisible || !e.IsVisible)
|
if (!e.Layer.IsVisible || !e.IsVisible)
|
||||||
@@ -501,8 +544,10 @@ namespace OpenNest.Controls
|
|||||||
sf.Alignment = text.HAlign;
|
sf.Alignment = text.HAlign;
|
||||||
sf.LineAlignment = text.VAlign;
|
sf.LineAlignment = text.VAlign;
|
||||||
|
|
||||||
|
var brush = TitleBlockEntityIds != null && TitleBlockEntityIds.Count > 0
|
||||||
|
? ghostTextBrush : textBrush;
|
||||||
using var font = new Font("Segoe UI", fontSize, GraphicsUnit.Pixel);
|
using var font = new Font("Segoe UI", fontSize, GraphicsUnit.Pixel);
|
||||||
g.DrawString(text.Value, font, textBrush, 0, 0, sf);
|
g.DrawString(text.Value, font, brush, 0, 0, sf);
|
||||||
g.Restore(state);
|
g.Restore(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace OpenNest.Controls
|
|||||||
public List<Entity> OriginalEntities { get; set; }
|
public List<Entity> OriginalEntities { get; set; }
|
||||||
public List<Bend> Bends { get; set; } = new();
|
public List<Bend> Bends { get; set; } = new();
|
||||||
public HashSet<Guid> SuppressedEntityIds { get; set; }
|
public HashSet<Guid> SuppressedEntityIds { get; set; }
|
||||||
|
public HashSet<Guid> TitleBlockEntityIds { get; set; }
|
||||||
public Box Bounds { get; set; }
|
public Box Bounds { get; set; }
|
||||||
public int EntityCount { get; set; }
|
public int EntityCount { get; set; }
|
||||||
public List<CadText> Texts { get; set; } = new();
|
public List<CadText> Texts { get; set; } = new();
|
||||||
|
|||||||
@@ -94,8 +94,16 @@ namespace OpenNest.Forms
|
|||||||
Bounds = result.Bounds,
|
Bounds = result.Bounds,
|
||||||
EntityCount = result.Entities.Count,
|
EntityCount = result.Entities.Count,
|
||||||
Texts = ExtractTexts(result.Document),
|
Texts = ExtractTexts(result.Document),
|
||||||
|
TitleBlockEntityIds = result.TitleBlockEntityIds,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (result.TitleBlockEntityIds != null && result.TitleBlockEntityIds.Count > 0)
|
||||||
|
{
|
||||||
|
item.SuppressedEntityIds ??= new HashSet<Guid>();
|
||||||
|
foreach (var id in result.TitleBlockEntityIds)
|
||||||
|
item.SuppressedEntityIds.Add(id);
|
||||||
|
}
|
||||||
|
|
||||||
if (InvokeRequired)
|
if (InvokeRequired)
|
||||||
BeginInvoke((Action)(() => fileList.AddItem(item)));
|
BeginInvoke((Action)(() => fileList.AddItem(item)));
|
||||||
else
|
else
|
||||||
@@ -154,6 +162,7 @@ namespace OpenNest.Forms
|
|||||||
entityView1.Entities.AddRange(item.Entities);
|
entityView1.Entities.AddRange(item.Entities);
|
||||||
entityView1.Bends = item.Bends ?? new List<Bend>();
|
entityView1.Bends = item.Bends ?? new List<Bend>();
|
||||||
entityView1.Texts = item.Texts ?? new List<CadText>();
|
entityView1.Texts = item.Texts ?? new List<CadText>();
|
||||||
|
entityView1.TitleBlockEntityIds = item.TitleBlockEntityIds;
|
||||||
|
|
||||||
item.Entities.ForEach(e => e.IsVisible = true);
|
item.Entities.ForEach(e => e.IsVisible = true);
|
||||||
if (item.Entities.Any(e => e.Layer != null))
|
if (item.Entities.Any(e => e.Layer != null))
|
||||||
|
|||||||
Reference in New Issue
Block a user