308 lines
10 KiB
C#
308 lines
10 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using System.IO;
|
|||
|
|
using ToolPathParser;
|
|||
|
|
|
|||
|
|
|
|||
|
|
namespace ClassicalCuttingForce
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 分情况进行经典切削力计算
|
|||
|
|
/// </summary>
|
|||
|
|
public class ClassicalCuttingForceCalculate
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 按照轴向切深分组进行切削力计算
|
|||
|
|
/// </summary>
|
|||
|
|
public 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();
|
|||
|
|
|
|||
|
|
// 遍历每个提取到的层
|
|||
|
|
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 == 1)
|
|||
|
|
{
|
|||
|
|
LineCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
|||
|
|
}
|
|||
|
|
else if (point.SegmentType == 2)
|
|||
|
|
{
|
|||
|
|
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 == 1)
|
|||
|
|
{
|
|||
|
|
LineCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
|||
|
|
}
|
|||
|
|
else if (point.SegmentType == 2)
|
|||
|
|
{
|
|||
|
|
if (point.K == 1)
|
|||
|
|
{
|
|||
|
|
ConcaveCurvesCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
|||
|
|
}
|
|||
|
|
else if (point.K == -1)
|
|||
|
|
{
|
|||
|
|
ConvexCurvesCalculator.CalculateCuttingForce(direction, point, coefficients, toolParams);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Console.WriteLine($"警告: 点数不足,无法判断方向");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
{
|
|||
|
|
List<List<ToolPosition>> 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();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|