Files
StructureHelper/StructureHelperCommon/Models/Shapes/Logics/Polyline2DToLinePoligonConvertLogic.cs
2025-11-03 13:58:27 +05:00

90 lines
3.5 KiB
C#

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 * metresToMillimeters / (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
};
}
}
}