From 8a0222e903f1acdebd38a60eafaa3de086ae4420 Mon Sep 17 00:00:00 2001
From: "Mr.Xia" <1424473282@qq.com>
Date: Mon, 3 Aug 2026 20:31:32 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20ROU=E9=87=8D=E5=A4=8D=E7=9B=B4=E5=BE=84?=
=?UTF-8?q?=E5=8E=BB=E9=87=8D=20=E2=80=94=20=E4=B8=A4=E9=81=8D=E6=89=AB?=
=?UTF-8?q?=E6=8F=8F=E5=88=86=E9=85=8D=E5=8F=82=E6=95=B0=E6=A7=BD=E4=BD=8D?=
=?UTF-8?q?=20+=20=E8=BE=93=E5=87=BA=E4=BF=AE=E6=94=B9=E7=9B=B4=E5=BE=84?=
=?UTF-8?q?=E6=96=87=E6=9C=AC=E4=B8=BA=E4=B8=89=E4=BD=8D=E5=B0=8F=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 两遍扫描算法:Pass1 收集头部所有刀径并分配参数键,Pass2 重建行并追加参数
- 输出直径文本规范化到三位小数,重复直径依次 +0.001
- 参数字典键和ROU直径均 Math.Round(x,3) 消除浮点精度问题
- 匹配策略:在 [baseDiam, baseDiam+0.01) 内找候选键,按序分配
---
RouParameterReplacer.cs | 139 +++++++++++++++++++++++++---------------
1 file changed, 88 insertions(+), 51 deletions(-)
diff --git a/RouParameterReplacer.cs b/RouParameterReplacer.cs
index d279ada..1e0632d 100644
--- a/RouParameterReplacer.cs
+++ b/RouParameterReplacer.cs
@@ -25,10 +25,10 @@ namespace DrillTools
///
/// 匹配 .rou 刀径定义行:T<刀号>C<直径>
- /// 例:T01C1.8
+ /// 例:T01C1.8 → prefix="T01C", diameter="1.8"
///
private static readonly Regex ToolLinePattern = new(
- @"^T\d+C(?\d+(?:\.\d+)?)",
+ @"^(?T\d+C)(?\d+(?:\.\d+)?)",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
///
@@ -84,6 +84,9 @@ namespace DrillTools
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
continue;
+ // 规范化到3位小数,消除浮点精度差异
+ diameter = Math.Round(diameter, 3);
+
string paramString = match.Groups["params"].Value;
// 如果同一直径出现多次,后面的覆盖前面的
@@ -95,37 +98,32 @@ namespace DrillTools
///
/// 在 .rou 文件内容中,将 M48~% 区域内的刀径行末尾追加参数。
+ /// 使用两遍扫描:第一遍按基准直径分组并从参数字典中分配可用键,第二遍执行替换。
///
private static string ReplaceParametersInContent(string rouContent, Dictionary paramDict, HashSet unmatchedDiameters)
{
var lines = DrillTapeLineParser.SplitLines(rouContent);
- var builder = new StringBuilder(rouContent.Length + 1024);
+ // === Pass 1: 收集头部区域所有刀径行,按基准直径分组,分配参数键 ===
bool inHeader = false;
+ var toolEntries = new List<(int Index, string Prefix, double Diameter)>();
- foreach (var line in lines)
+ for (int i = 0; i < lines.Count; i++)
{
- string trimmed = line.Text.Trim();
+ string trimmed = lines[i].Text.Trim();
- // 检测 M48 头部开始
if (!inHeader && trimmed == "M48")
{
inHeader = true;
- builder.Append(line.Text);
- builder.Append(line.LineEnding);
continue;
}
- // 检测 % 头部结束
if (inHeader && trimmed == "%")
{
inHeader = false;
- builder.Append(line.Text);
- builder.Append(line.LineEnding);
continue;
}
- // 在头部区域内匹配刀径定义行
if (inHeader)
{
var match = ToolLinePattern.Match(trimmed);
@@ -133,54 +131,93 @@ namespace DrillTools
double.TryParse(match.Groups["diameter"].Value,
NumberStyles.Float, CultureInfo.InvariantCulture, out double diameter))
{
- // 按直径数值匹配参数(允许浮点误差)
- string? paramString = FindMatchingParameter(diameter, paramDict);
- if (paramString != null)
- {
- // 保留原始行尾空白
- string textWithoutTrailing = line.Text.TrimEnd();
- int trailingLength = line.Text.Length - textWithoutTrailing.Length;
- string trailing = trailingLength > 0 ? line.Text[^trailingLength..] : string.Empty;
+ diameter = Math.Round(diameter, 3);
+ string prefix = match.Groups["prefix"].Value;
+ toolEntries.Add((i, prefix, diameter));
+ }
+ }
+ }
- builder.Append(textWithoutTrailing);
- builder.Append(paramString);
- builder.Append(trailing);
- builder.Append(line.LineEnding);
- continue;
- }
- else
+ // 按基准直径分组,为每组分配显示直径和参数键
+ // lineIndex -> (prefix, displayDiameter, assignedParamKey | null)
+ var toolMap = new Dictionary();
+
+ foreach (var group in toolEntries.GroupBy(e => e.Diameter))
+ {
+ double baseDiam = group.Key;
+ var occurrences = group.ToList();
+
+ // 在 [baseDiam, baseDiam + 0.01) 范围内查找候选参数键
+ var candidates = paramDict.Keys
+ .Where(k => k >= baseDiam && k < baseDiam + 0.01)
+ .OrderBy(k => k)
+ .ToList();
+
+ // 如果没有候选键,尝试容差回退(仅对首个出现)
+ if (candidates.Count == 0)
+ {
+ foreach (var kvp in paramDict)
+ {
+ if (Math.Abs(kvp.Key - baseDiam) < DiameterTolerance)
{
- unmatchedDiameters.Add(diameter);
+ candidates.Add(kvp.Key);
+ break;
}
}
}
- // 其他行原样保留
- builder.Append(line.Text);
- builder.Append(line.LineEnding);
+ // 分配:每个出现的显示直径为 baseDiam + j*0.001(3位小数补零)
+ // 参数键从候选列表中按序取,超出则为 null(未匹配)
+ for (int j = 0; j < occurrences.Count; j++)
+ {
+ double displayDiameter = Math.Round(baseDiam + j * 0.001, 3);
+ double? assignedKey = j < candidates.Count ? candidates[j] : null;
+ toolMap[occurrences[j].Index] = (occurrences[j].Prefix, displayDiameter, assignedKey);
+ }
+ }
+
+ // === Pass 2: 执行替换(修改直径文本 + 追加参数) ===
+ var builder = new StringBuilder(rouContent.Length + 1024);
+
+ for (int i = 0; i < lines.Count; i++)
+ {
+ if (toolMap.TryGetValue(i, out var toolInfo))
+ {
+ if (toolInfo.ParamKey.HasValue)
+ {
+ string paramString = paramDict[toolInfo.ParamKey.Value];
+
+ // 重建行:prefix + 显示直径 + 参数
+ string displayDiam = toolInfo.DisplayDiameter.ToString("F3", CultureInfo.InvariantCulture);
+
+ // 保留行尾空白
+ string originalLine = lines[i].Text.TrimEnd();
+ int trailingLength = lines[i].Text.Length - originalLine.Length;
+ string trailing = trailingLength > 0 ? lines[i].Text[^trailingLength..] : string.Empty;
+
+ builder.Append(toolInfo.Prefix);
+ builder.Append(displayDiam);
+ builder.Append(paramString);
+ builder.Append(trailing);
+ builder.Append(lines[i].LineEnding);
+ }
+ else
+ {
+ // 未匹配:保留原始行,记录直径
+ unmatchedDiameters.Add(toolInfo.DisplayDiameter);
+ builder.Append(lines[i].Text);
+ builder.Append(lines[i].LineEnding);
+ }
+ }
+ else
+ {
+ // 非刀径行原样保留
+ builder.Append(lines[i].Text);
+ builder.Append(lines[i].LineEnding);
+ }
}
return builder.ToString();
}
-
- ///
- /// 按直径查找匹配的参数字符串,允许 0.001 的浮点误差。
- /// 返回 null 表示未找到匹配。
- ///
- private static string? FindMatchingParameter(double diameter, Dictionary paramDict)
- {
- // 精确匹配优先
- if (paramDict.TryGetValue(diameter, out string? paramString))
- return paramString;
-
- // 浮点容差匹配
- foreach (var kvp in paramDict)
- {
- if (Math.Abs(kvp.Key - diameter) < DiameterTolerance)
- return kvp.Value;
- }
-
- return null;
- }
}
}