291 lines
11 KiB
C#
291 lines
11 KiB
C#
using CaeKnowledge.Data;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using ToolPathParser;
|
||
|
||
namespace CaeCuttingForce.FlatendCutter
|
||
{
|
||
/// <summary>
|
||
/// 分情况进行经典切削力计算
|
||
/// </summary>
|
||
public static class ClassicalCuttingForceCalculate
|
||
{
|
||
/// <summary>
|
||
/// 按照轴向切深分组进行切削力计算
|
||
/// </summary>
|
||
public static void AxialDepthGroupedForceCalculator(List<ToolPosition> toolPosList, CuttingForceCoefficients coefficients, ToolParameters toolParams)
|
||
{
|
||
// 到位方向
|
||
int direction = SimpleMillingDirectionAnalyzer.AnalyzeDirection(toolPosList);
|
||
|
||
List<List<ToolPosition>> allLayers = LayerSplitter.SplitIntoLayersByZ(toolPosList);
|
||
|
||
List<List<ToolPosition>> firstDepthLayers = LayerSplitter.ExtractFirstAxialDepthLayers(allLayers);
|
||
|
||
var LineCalculator = new LineForce();
|
||
|
||
var ConcaveCurvesCalculator = new ConcaveCurvesForce();
|
||
|
||
var ConvexCurvesCalculator = new ConvexCurvesForce();
|
||
|
||
// 遍历每个提取到的层
|
||
// ReSharper disable once ForCanBeConvertedToForeach
|
||
for (int i = 0; i < firstDepthLayers.Count; i++)
|
||
{
|
||
// 每个 firstDepthLayers[i] 就是对应层的 List<ToolPosition>
|
||
List<ToolPosition> targetLayer = firstDepthLayers[i];
|
||
|
||
double targetDepth = targetLayer[0].AxialDepth;
|
||
|
||
int layerIndexInAllLayers = allLayers.IndexOf(targetLayer) + 1;
|
||
|
||
foreach (var point in targetLayer)
|
||
{
|
||
if (direction == 1)//逆时针
|
||
{
|
||
if (point.SegmentType == EnumSegmentType.Line) // By Luke修改
|
||
{
|
||
LineCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
||
}
|
||
else if (point.SegmentType == EnumSegmentType.Arc) // By Luke修改
|
||
{
|
||
if (point.K == -1)
|
||
{
|
||
ConcaveCurvesCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
||
}
|
||
else if (point.K == 1)
|
||
{
|
||
ConvexCurvesCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
||
}
|
||
|
||
}
|
||
}
|
||
else if (direction == 2)//顺时针
|
||
{
|
||
if (point.SegmentType == EnumSegmentType.Line) // By Luke修改
|
||
{
|
||
LineCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
||
}
|
||
else if (point.SegmentType == EnumSegmentType.Arc) // By Luke修改
|
||
{
|
||
if (point.K == 1)
|
||
{
|
||
ConcaveCurvesCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
||
}
|
||
else if (point.K == -1)
|
||
{
|
||
ConvexCurvesCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Console.WriteLine($"警告: 点数不足,无法判断方向");
|
||
throw new Exception($"警告: 点数不足,无法判断方向");
|
||
}
|
||
|
||
}
|
||
|
||
new CuttingForceMapper().MapFirstLayerForceToSameDepth(allLayers, firstDepthLayers);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 利用刀位点判断铣削方向
|
||
/// </summary>
|
||
public class SimpleMillingDirectionAnalyzer
|
||
{
|
||
public const int Clockwise = 2; // 顺时针
|
||
public const int CounterClockwise = 1; // 逆时针
|
||
public const int Undetermined = 0; // 无法确定
|
||
|
||
public static int AnalyzeDirection(List<ToolPosition> allPoints)
|
||
{
|
||
if (allPoints == null || allPoints.Count < 3)
|
||
return Undetermined;
|
||
|
||
double targetZ = allPoints[0].Z;
|
||
|
||
List<ToolPosition> layerPoints = new List<ToolPosition>();
|
||
foreach (ToolPosition point in allPoints)
|
||
{
|
||
if (point.Z == targetZ)
|
||
{
|
||
layerPoints.Add(point);
|
||
}
|
||
}
|
||
|
||
if (layerPoints.Count < 3)
|
||
{
|
||
//Console.WriteLine($"警告: Z={targetZ}层的点数不足({layerPoints.Count}),无法判断方向");
|
||
return Undetermined;
|
||
}
|
||
|
||
double area = CalculateSignedArea(layerPoints);
|
||
|
||
if (Math.Abs(area) < 1e-10)
|
||
return Undetermined;
|
||
|
||
return area > 0 ? CounterClockwise : Clockwise;
|
||
}
|
||
|
||
// 计算有向面积
|
||
private static double CalculateSignedArea(List<ToolPosition> points)
|
||
{
|
||
double area = 0;
|
||
int n = points.Count;
|
||
|
||
for (int i = 0; i < n; i++)
|
||
{
|
||
ToolPosition current = points[i];
|
||
ToolPosition next = points[(i + 1) % n];
|
||
|
||
area += current.X * next.Y - next.X * current.Y;
|
||
}
|
||
|
||
return area / 2.0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按刀位点原始顺序分层,提取所需要层
|
||
/// </summary>
|
||
public static class LayerSplitter
|
||
{
|
||
private const double Z_TOLERANCE = 0.001;
|
||
|
||
/// <summary>
|
||
/// 按刀位点原始顺序分层(先遇到的Z值先成为层)
|
||
/// </summary>
|
||
public static List<List<ToolPosition>> SplitIntoLayersByZ(List<ToolPosition> allPoints)
|
||
{
|
||
var allLayers = new List<List<ToolPosition>>();
|
||
|
||
if (allPoints == null || allPoints.Count == 0)
|
||
return allLayers;
|
||
|
||
foreach (var point in allPoints)
|
||
{
|
||
var existingLayer = allLayers.FirstOrDefault(layer => Math.Abs(layer[0].Z - point.Z) <= Z_TOLERANCE);
|
||
|
||
if (existingLayer != null)
|
||
{
|
||
existingLayer.Add(point);
|
||
}
|
||
else
|
||
{
|
||
allLayers.Add(new List<ToolPosition> { point });
|
||
}
|
||
}
|
||
|
||
return allLayers;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从已分层的 allLayers 中,提取每个轴向切深首次出现的层
|
||
/// </summary>
|
||
/// <param name="allLayers">已按原始顺序分层的刀位点(外层=层,内层=该层点)</param>
|
||
/// <returns>每个轴向切深首次出现的层集合(List<List<ToolPosition>>)</returns>
|
||
public static List<List<ToolPosition>> ExtractFirstAxialDepthLayers(List<List<ToolPosition>> allLayers)
|
||
{
|
||
// 存储已出现过的轴向切深(用于去重,判断是否首次出现)
|
||
HashSet<double> existedAxialDepths = new HashSet<double>();
|
||
|
||
List<List<ToolPosition>> firstDepthLayers = new List<List<ToolPosition>>();
|
||
|
||
const double AXIAL_DEPTH_TOLERANCE = 0.001;
|
||
|
||
// 空值判断,避免报错
|
||
if (allLayers == null || allLayers.Count == 0)
|
||
return firstDepthLayers;
|
||
|
||
// 按原始顺序遍历 allLayers 中的每一层
|
||
foreach (var currentLayer in allLayers)
|
||
{
|
||
|
||
if (currentLayer == null || currentLayer.Count == 0)
|
||
continue;
|
||
|
||
double currentAxialDepth = currentLayer[0].AxialDepth;
|
||
|
||
bool isFirstAppearance = !existedAxialDepths
|
||
.Any(depth => Math.Abs(depth - currentAxialDepth) <= AXIAL_DEPTH_TOLERANCE);
|
||
|
||
if (isFirstAppearance)
|
||
{
|
||
|
||
existedAxialDepths.Add(currentAxialDepth);
|
||
|
||
firstDepthLayers.Add(currentLayer);
|
||
}
|
||
}
|
||
|
||
return firstDepthLayers;
|
||
}
|
||
|
||
}
|
||
|
||
public class CuttingForceMapper
|
||
{
|
||
private const double TOLERANCE = 0.001;
|
||
|
||
/// <summary>
|
||
/// 首层切削力映射到同切深其他层(按点索引复制)
|
||
/// </summary>
|
||
/// <param name="allLayers">所有分层(含首层+其他层)</param>
|
||
/// <param name="firstDepthLayers">已计算切削力的首层集合</param>
|
||
public void MapFirstLayerForceToSameDepth(List<List<ToolPosition>> allLayers, List<List<ToolPosition>> firstDepthLayers)
|
||
{
|
||
// 构建「切深→首层」映射
|
||
var depthFirstLayerMap = firstDepthLayers
|
||
.Where(layer => layer != null && layer.Count > 0)
|
||
.ToDictionary(
|
||
layer => layer[0].AxialDepth,
|
||
layer => layer,
|
||
new DoubleEqualityComparer(TOLERANCE)
|
||
);
|
||
|
||
// 遍历所有层,复制切削力
|
||
foreach (var currentLayer in allLayers)
|
||
{
|
||
if (currentLayer == null || currentLayer.Count == 0) continue;
|
||
|
||
double currentDepth = currentLayer[0].AxialDepth;
|
||
|
||
if (!depthFirstLayerMap.TryGetValue(currentDepth, out var firstLayer) || firstDepthLayers.Contains(currentLayer))
|
||
continue;
|
||
|
||
if (firstLayer.Count != currentLayer.Count) continue;
|
||
|
||
for (int i = 0; i < firstLayer.Count; i++)
|
||
{
|
||
var firstPoint = firstLayer[i];
|
||
var currentPoint = currentLayer[i];
|
||
|
||
currentPoint.AverageFx = firstPoint.AverageFx;
|
||
currentPoint.AverageFy = firstPoint.AverageFy;
|
||
currentPoint.AverageFz = firstPoint.AverageFz;
|
||
currentPoint.MaxFx = firstPoint.MaxFx;
|
||
currentPoint.MaxFy = firstPoint.MaxFy;
|
||
currentPoint.MaxFz = firstPoint.MaxFz;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 自定义浮点数比较器
|
||
/// </summary>
|
||
private class DoubleEqualityComparer : IEqualityComparer<double>
|
||
{
|
||
private readonly double _tolerance;
|
||
public DoubleEqualityComparer(double tolerance) => _tolerance = tolerance;
|
||
public bool Equals(double x, double y) => Math.Abs(x - y) <= _tolerance;
|
||
public int GetHashCode(double obj) => obj.ToString("F3").GetHashCode();
|
||
}
|
||
}
|
||
|
||
}
|