Add import of polygon from dxf

This commit is contained in:
Evgeny Redikultsev
2025-11-01 21:56:47 +05:00
parent 3dfbb43b73
commit ba0d3e580b
34 changed files with 498 additions and 179 deletions

View File

@@ -29,5 +29,7 @@
public static string CalculationError => "#0019: Error of calculation";
public static string SourceObject => "#0020: Source object";
public static string TargetObject => "#0021: Target object";
public static string FileDoesNotExsist => "#0022: File does not exist";
public static string FileCantBeOpened => "#0023: File can not be opened";
}
}

View File

@@ -6,8 +6,18 @@ using System.Threading.Tasks;
namespace StructureHelperCommon.Infrastructures.Interfaces
{
/// <summary>
/// Implements logic for convert from one object to another one
/// </summary>
/// <typeparam name="T">Type of target object</typeparam>
/// <typeparam name="V">Type of source object</typeparam>
public interface IObjectConvertStrategy<T, V>
{
/// <summary>
/// Converts sourve object to another one
/// </summary>
/// <param name="source">Source object</param>
/// <returns>Target object</returns>
T Convert(V source);
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Models.Shapes
{
public enum ArcFlatteningMode
{
ByAngleStep,
BySegmentCount,
ByMaxSegmentLength,
Combined
}
public class ArcFlatteningOption : IArcFlatteningOption
{
public ArcFlatteningMode Mode { get; set; } = ArcFlatteningMode.Combined;
public double AngleStepRadians { get; set; } = Math.PI / 8; // 22.5°
public int SegmentCount { get; set; } = 2;
public double MaxSegmentLength { get; set; } = 0.05; // (m)
}
}

View File

@@ -0,0 +1,10 @@
namespace StructureHelperCommon.Models.Shapes
{
public interface IArcFlatteningOption
{
double AngleStepRadians { get; set; }
double MaxSegmentLength { get; set; }
ArcFlatteningMode Mode { get; set; }
int SegmentCount { get; set; }
}
}

View File

@@ -0,0 +1,89 @@
using netDxf.Entities;
using StructureHelperCommon.Infrastructures.Interfaces;
using System;
namespace StructureHelperCommon.Models.Shapes
{
public class Polyline2DToLinePoligonConvertLogic : IObjectConvertStrategy<LinePolygonShape, Polyline2D>
{
private const double metresToMillimeters = 1000.0;
private IArcFlatteningOption options = new ArcFlatteningOption();
public LinePolygonShape Convert(Polyline2D source)
{
var polygon = new LinePolygonShape(Guid.NewGuid());
for (int i = 0; i < source.Vertexes.Count; i++)
{
var v1 = source.Vertexes[i];
var v2 = source.Vertexes[(i + 1) % source.Vertexes.Count];
polygon.AddVertex(new Vertex(v1.Position.X / metresToMillimeters, v1.Position.Y / metresToMillimeters));
double bulge = v1.Bulge;
if (Math.Abs(bulge) < 1e-9)
continue;
ProcessBulge(polygon, v1, v2, bulge);
}
polygon.IsClosed = source.IsClosed;
return polygon;
}
private void ProcessBulge(LinePolygonShape polygon, Polyline2DVertex v1, Polyline2DVertex v2, double bulge)
{
var p1 = v1.Position;
var p2 = v2.Position;
double chord = Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
double theta = 4 * Math.Atan(Math.Abs(bulge));
double radius = chord / (2 * Math.Sin(theta / 2));
double dir = bulge > 0 ? 1 : -1;
// Compute arc center
double mx = (p1.X + p2.X) / 2;
double my = (p1.Y + p2.Y) / 2;
double nx = -(p2.Y - p1.Y) / chord;
double ny = (p2.X - p1.X) / chord;
double d = radius * Math.Cos(theta / 2);
double cx = mx + dir * nx * d;
double cy = my + dir * ny * d;
double a1 = Math.Atan2(p1.Y - cy, p1.X - cx);
double a2 = Math.Atan2(p2.Y - cy, p2.X - cx);
double totalAngle = a2 - a1;
if (dir > 0 && totalAngle < 0) totalAngle += 2 * Math.PI;
if (dir < 0 && totalAngle > 0) totalAngle -= 2 * Math.PI;
double stepAngle = GetEffectiveStepAngle(radius, Math.Abs(totalAngle), options);
int segments = Math.Max(2, (int)Math.Ceiling(Math.Abs(totalAngle) / stepAngle));
for (int j = 1; j < segments; j++)
{
double a = a1 + totalAngle * j / segments;
double x = cx + radius * Math.Cos(a);
double y = cy + radius * Math.Sin(a);
polygon.AddVertex(new Vertex(x / metresToMillimeters, y / metresToMillimeters));
}
}
private static double GetEffectiveStepAngle(double radius, double arcAngle, IArcFlatteningOption options)
{
// compute angle from each criterion
double byAngle = options.AngleStepRadians;
double byCount = arcAngle / options.SegmentCount;
double byLength = 2 * Math.Asin(options.MaxSegmentLength / (2 * radius));
return options.Mode switch
{
ArcFlatteningMode.ByAngleStep => byAngle,
ArcFlatteningMode.BySegmentCount => byCount,
ArcFlatteningMode.ByMaxSegmentLength => byLength,
ArcFlatteningMode.Combined => Math.Min(byAngle, Math.Min(byCount, byLength)),
_ => byAngle
};
}
}
}

View File

@@ -0,0 +1,50 @@
using StructureHelperCommon.Infrastructures.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports.Factories
{
public enum FileInputDataType
{
Csv,
Png,
Dxf
}
public static class FileInputDataFactory
{
public static FileIOInputData GetFileIOInputData(FileInputDataType dataType)
{
if (dataType == FileInputDataType.Csv)
{
return new FileIOInputData
{
Filter = "csv |*.csv",
Title = "Save in csv File"
};
}
else if (dataType == FileInputDataType.Png)
{
return new FileIOInputData
{
Filter = "png |*.png",
Title = "Save in *.png File"
};
}
else if (dataType == FileInputDataType.Dxf)
{
return new FileIOInputData
{
Filter = "dxf |*.dxf",
Title = "Save in *.dxf File"
};
}
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(dataType));
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace StructureHelperCommon.Services.Exports
{
public class FileIOInputData : IFileIOnputData
{
public string FileName { get; set; } = "New file";
public string Filter { get; set; }
public string Title { get; set; }
}
}

View File

@@ -0,0 +1,54 @@
using netDxf;
using netDxf.Tables;
using StructureHelperCommon.Infrastructures.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports
{
public class GetDxfLayerLogic : IGetDxfLayerLogic
{
private const string PrimitivesLayer = "STR-PRM";
private const string OpeningsLayer = "STR-OPN";
private const string RebarLayer = "STR-REB";
private const string LayerNameIsUnKnown = ": Layer name is unknown";
public Layer GetOrCreateLayer(DxfDocument dxf, LayerNames layerName)
{
string newLayerName = GetLayerName(layerName);
Layer newLayer = dxf.Layers.Contains(newLayerName)
? dxf.Layers[newLayerName]
: new Layer(newLayerName)
{
Color = GetLayerColor(layerName)
};
if (!dxf.Layers.Contains(newLayerName))
dxf.Layers.Add(newLayer);
return newLayer;
}
public string GetLayerName(LayerNames layerName)
{
if (layerName == LayerNames.StructiralPrimitives) { return PrimitivesLayer; }
else if (layerName == LayerNames.StructuralOpenings) { return OpeningsLayer; }
else if (layerName == LayerNames.StructuralRebars) { return RebarLayer; }
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(layerName) + LayerNameIsUnKnown);
}
}
public AciColor GetLayerColor(LayerNames layerName)
{
if (layerName == LayerNames.StructiralPrimitives) { return AciColor.Blue; }
else if (layerName == LayerNames.StructuralOpenings) { return AciColor.DarkGray; }
else if (layerName == LayerNames.StructuralRebars) { return AciColor.Magenta; }
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(layerName) + LayerNameIsUnKnown);
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace StructureHelperCommon.Services.Exports
{
public interface IExportLogic
{
void Export();
}
}

View File

@@ -6,9 +6,8 @@ using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports
{
public interface IExportResultLogic
public interface IExportToFileLogic : IExportLogic
{
string FileName { get; set; }
void Export();
}
}

View File

@@ -0,0 +1,9 @@
namespace StructureHelperCommon.Services.Exports
{
public interface IFileIOnputData
{
string FileName { get; set; }
string Filter { get; set; }
string Title { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using netDxf;
using netDxf.Tables;
namespace StructureHelperCommon.Services.Exports
{
public interface IGetDxfLayerLogic
{
AciColor GetLayerColor(LayerNames layerName);
string GetLayerName(LayerNames layerName);
Layer GetOrCreateLayer(DxfDocument dxf, LayerNames layerName);
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports
{
public interface IImportFromFileLogic : IImportLogic
{
string FileName { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports
{
public interface IImportLogic
{
void Import();
}
}

View File

@@ -0,0 +1,31 @@
using netDxf;
using netDxf.Entities;
using netDxf.Header;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports
{
public class ShapesAllImportFromDxfLogic : IImportFromFileLogic
{
public string FileName { get; set; }
public List<EntityObject> Entities { get; set; } = [];
public void Import()
{
// this check is optional but recommended before loading a DXF file
DxfVersion dxfVersion = DxfDocument.CheckDxfFileVersion(FileName);
// netDxf is only compatible with AutoCad2000 and higher DXF versions
if (dxfVersion < DxfVersion.AutoCad2000) return;
// load file
DxfDocument dxf = DxfDocument.Load(FileName);
Entities.Clear();
if (dxf != null)
{
Entities.AddRange(dxf.Entities.All);
}
}
}
}

View File

@@ -3,11 +3,7 @@ using netDxf.Entities;
using netDxf.Tables;
using StructureHelperCommon.Infrastructures.Exceptions;
using StructureHelperCommon.Models.Shapes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StructureHelperCommon.Services.Exports
{
@@ -17,14 +13,12 @@ namespace StructureHelperCommon.Services.Exports
StructuralOpenings,
StructuralRebars
}
public class ShapesExportToDxfLogic : IExportResultLogic
public class ShapesExportToDxfLogic : IExportToFileLogic
{
private const string PrimitivesLayer = "STR-PRM";
private const string OpeningsLayer = "STR-OPN";
private const string RebarLayer = "STR-REB";
private const string LayerNameIsUnKnown = ": Layer name is unknown";
private const string ShapeTypeIsUnknown = ": Shape is unknown";
private const double metresToMillimeters = 1000.0;
private readonly List<(IShape shape, LayerNames layerName)> shapes = [];
private IGetDxfLayerLogic layerLogic = new GetDxfLayerLogic();
public ShapesExportToDxfLogic(List<(IShape shape, LayerNames layerName)> shapes)
{
@@ -45,19 +39,12 @@ namespace StructureHelperCommon.Services.Exports
{
ProcessShape(dxf, shape);
}
Layer primitiveLayer = new Layer(PrimitivesLayer)
{
Color = AciColor.Blue
};
dxf.Layers.Add(primitiveLayer);
// save to file
dxf.Save(FileName);
}
private void ProcessShape(DxfDocument dxf, (IShape shape, LayerNames layerName) shape)
{
Layer layer = GetOrCreateLayer(dxf, shape.layerName);
Layer layer = layerLogic.GetOrCreateLayer(dxf, shape.layerName);
ProcessShape(dxf, shape.shape, layer);
}
@@ -78,7 +65,7 @@ namespace StructureHelperCommon.Services.Exports
List<Polyline2DVertex> polylineVertices = [];
foreach (var item in linePolygon.Vertices)
{
Polyline2DVertex vertex = new Polyline2DVertex(item.Point.X, item.Point.Y);
Polyline2DVertex vertex = new Polyline2DVertex(item.Point.X * metresToMillimeters, item.Point.Y * metresToMillimeters);
polylineVertices.Add(vertex);
}
Polyline2D polyline2D = new Polyline2D(polylineVertices)
@@ -88,42 +75,5 @@ namespace StructureHelperCommon.Services.Exports
};
dxf.Entities.Add(polyline2D);
}
private Layer GetOrCreateLayer(DxfDocument dxf, LayerNames layerName)
{
string newLayerName = GetLayerName(layerName);
Layer newLayer = dxf.Layers.Contains("newLayerName")
? dxf.Layers["newLayerName"]
: new Layer("newLayerName")
{
Color = GetLayerColor(layerName)
};
if (!dxf.Layers.Contains(newLayerName))
dxf.Layers.Add(newLayer);
return newLayer;
}
private string GetLayerName(LayerNames layerName)
{
if (layerName == LayerNames.StructiralPrimitives) { return PrimitivesLayer; }
else if (layerName == LayerNames.StructuralOpenings) { return OpeningsLayer; }
else if (layerName == LayerNames.StructuralRebars) { return RebarLayer; }
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(layerName) + LayerNameIsUnKnown);
}
}
private AciColor GetLayerColor(LayerNames layerName)
{
if (layerName == LayerNames.StructiralPrimitives) { return AciColor.Blue; }
else if (layerName == LayerNames.StructuralOpenings) { return AciColor.DarkGray; }
else if (layerName == LayerNames.StructuralRebars) { return AciColor.Magenta; }
else
{
throw new StructureHelperException(ErrorStrings.ObjectTypeIsUnknownObj(layerName) + LayerNameIsUnKnown);
}
}
}
}

View File

@@ -0,0 +1,18 @@
using netDxf.Entities;
using System.Linq;
namespace StructureHelperCommon.Services.Exports
{
public class SinglePolyline2DImportFromDxfLogic : IImportFromFileLogic
{
public string FileName { get; set; }
public Polyline2D Polyline2D { get; private set; }
public void Import()
{
var importLogic = new ShapesAllImportFromDxfLogic() { FileName = FileName};
importLogic.Import();
var entities = importLogic.Entities;
Polyline2D = entities.Single(x => x is Polyline2D) as Polyline2D;
}
}
}