From c7f982c145f131fbb0af1df7ea8b84b03fc56123 Mon Sep 17 00:00:00 2001
From: "Mr.Xia" <1424473282@qq.com>
Date: Mon, 25 May 2026 16:35:41 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A03.203=E5=AD=94PP?=
=?UTF-8?q?=E9=92=BB=E5=B8=A6=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
MainWindowViewModel.cs | 179 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 160 insertions(+), 19 deletions(-)
diff --git a/MainWindowViewModel.cs b/MainWindowViewModel.cs
index 2736720..54cb469 100644
--- a/MainWindowViewModel.cs
+++ b/MainWindowViewModel.cs
@@ -46,6 +46,8 @@ namespace DrillTools
private const int SvsiEnsureVisible = 0x8;
private const int SvsiFocused = 0x10;
private const int SelectFileFlags = SvsiSelect | SvsiDeselectOthers | SvsiEnsureVisible | SvsiFocused;
+ private const double PpSourceDiameter = 3.203;
+ private const int PpSideCoordinateTolerance = 1500;
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
@@ -1179,27 +1181,20 @@ M30";
/// 生成的文件路径
public string GeneratePpDrillTape()
{
- var sourceTool = GetPpSourceTool();
- if (sourceTool == null)
+ var ppRule = GetPpGenerationRule();
+ if (ppRule == null)
throw new InvalidOperationException("当前钻带不满足生成 PP 钻带条件");
- var locations = sourceTool.HoleLocations
- .Where(location => !string.IsNullOrWhiteSpace(location))
- .Take(2)
- .Select(location => location.Trim())
- .ToList();
-
- if (locations.Count < 2)
- throw new InvalidOperationException("3.203 刀具坐标不足,无法生成 PP 钻带");
-
string outputFilePath = GetPpDrillTapeOutputPath();
var ppDrillTape = new StringBuilder();
ppDrillTape.AppendLine("M48");
- ppDrillTape.AppendLine("T01C4.");
+ ppDrillTape.AppendLine(ppRule.ToolDefinition);
ppDrillTape.AppendLine("%");
ppDrillTape.AppendLine("T01");
- ppDrillTape.AppendLine(locations[0]);
- ppDrillTape.AppendLine(locations[1]);
+ foreach (var location in ppRule.Locations)
+ {
+ ppDrillTape.AppendLine(location);
+ }
ppDrillTape.AppendLine("M30");
File.WriteAllText(outputFilePath, ppDrillTape.ToString(), CreateGb2312Encoding());
@@ -1210,16 +1205,28 @@ M30";
private void UpdatePpDrillTapeState()
{
- CanGeneratePpDrillTape = GetPpSourceTool() != null;
+ CanGeneratePpDrillTape = GetPpGenerationRule() != null;
}
- private ToolItem? GetPpSourceTool()
+ private PpGenerationRule? GetPpGenerationRule()
{
- if (!IsStartupDrillTapeFile || !HasOriginalFile || OriginalTools.Count < 2)
+ if (!HasOriginalFile || OriginalTools.Count == 0)
+ return null;
+
+ var legacyRule = GetLegacyPpGenerationRule();
+ if (legacyRule != null)
+ return legacyRule;
+
+ return GetOuterPpGenerationRule();
+ }
+
+ private PpGenerationRule? GetLegacyPpGenerationRule()
+ {
+ if (!IsStartupDrillTapeFile || OriginalTools.Count < 2)
return null;
var sourceTool = OriginalTools[OriginalTools.Count - 2];
- if (Math.Abs(sourceTool.Diameter - 3.203) >= 0.001)
+ if (Math.Abs(sourceTool.Diameter - PpSourceDiameter) >= 0.001)
return null;
if (sourceTool.TotalHoles != 3)
@@ -1228,7 +1235,141 @@ M30";
if (sourceTool.HoleLocations == null || sourceTool.HoleLocations.Count < 2)
return null;
- return sourceTool;
+ var locations = sourceTool.HoleLocations
+ .Where(location => !string.IsNullOrWhiteSpace(location))
+ .Take(2)
+ .Select(location => location.Trim())
+ .ToList();
+
+ return locations.Count == 2
+ ? new PpGenerationRule("T01C4.", locations)
+ : null;
+ }
+
+ private PpGenerationRule? GetOuterPpGenerationRule()
+ {
+ var sourceTool = OriginalTools.FirstOrDefault(tool =>
+ Math.Abs(tool.Diameter - PpSourceDiameter) < 0.001
+ && (tool.TotalHoles == 13 || tool.TotalHoles == 15)
+ && tool.HoleLocations != null
+ && tool.HoleLocations.Count >= 4
+ && CanSelectPpDrillTapeLocations(tool));
+
+ return sourceTool == null
+ ? null
+ : new PpGenerationRule("T01C4.000", SelectPpDrillTapeLocations(sourceTool));
+ }
+
+ ///
+ /// 从 3.203 外框孔中按右、上、左、下四边各取一个中位孔,并保持原钻带坐标文本。
+ ///
+ private static List SelectPpDrillTapeLocations(ToolItem sourceTool)
+ {
+ var points = ParsePpHolePoints(sourceTool.HoleLocations);
+ if (points.Count < 4)
+ throw new InvalidOperationException("3.203 刀具坐标不足,无法生成 PP 钻带");
+
+ int minX = points.Min(point => point.X);
+ int maxX = points.Max(point => point.X);
+ int minY = points.Min(point => point.Y);
+ int maxY = points.Max(point => point.Y);
+
+ var selected = new[]
+ {
+ SelectMiddlePoint(points.Where(point => Math.Abs(point.X - maxX) <= PpSideCoordinateTolerance), sortByX: false, useUpperMiddle: true),
+ SelectMiddlePoint(points.Where(point => Math.Abs(point.Y - maxY) <= PpSideCoordinateTolerance), sortByX: true, useUpperMiddle: false),
+ SelectMiddlePoint(points.Where(point => Math.Abs(point.X - minX) <= PpSideCoordinateTolerance), sortByX: false, useUpperMiddle: true),
+ SelectMiddlePoint(points.Where(point => Math.Abs(point.Y - minY) <= PpSideCoordinateTolerance), sortByX: true, useUpperMiddle: false)
+ };
+
+ if (selected.Any(point => point == null))
+ throw new InvalidOperationException("3.203 刀具未形成完整的上下左右四边孔位,无法生成 PP 钻带");
+
+ var selectedTexts = selected
+ .Select(point => point!.Text)
+ .Distinct(StringComparer.Ordinal)
+ .ToHashSet(StringComparer.Ordinal);
+
+ if (selectedTexts.Count != 4)
+ throw new InvalidOperationException("3.203 刀具四边中位孔存在重复,无法生成 PP 钻带");
+
+ // 输出顺序沿用原 3.203 刀具中的孔位顺序,避免改变已有钻带坐标排序习惯。
+ return points
+ .Where(point => selectedTexts.Contains(point.Text))
+ .Select(point => point.Text)
+ .ToList();
+ }
+
+ private static bool CanSelectPpDrillTapeLocations(ToolItem sourceTool)
+ {
+ try
+ {
+ return SelectPpDrillTapeLocations(sourceTool).Count == 4;
+ }
+ catch (InvalidOperationException)
+ {
+ return false;
+ }
+ }
+
+ private static List ParsePpHolePoints(IEnumerable holeLocations)
+ {
+ var points = new List();
+ foreach (var location in holeLocations)
+ {
+ if (string.IsNullOrWhiteSpace(location))
+ continue;
+
+ var match = Regex.Match(location.Trim(), @"X([+-]?\d+)Y([+-]?\d+)");
+ if (!match.Success)
+ continue;
+
+ points.Add(new PpHolePoint(
+ location.Trim(),
+ int.Parse(match.Groups[1].Value),
+ int.Parse(match.Groups[2].Value)));
+ }
+
+ return points;
+ }
+
+ private static PpHolePoint? SelectMiddlePoint(IEnumerable sourcePoints, bool sortByX, bool useUpperMiddle)
+ {
+ var points = sortByX
+ ? sourcePoints.OrderBy(point => point.X).ThenBy(point => point.Y).ToList()
+ : sourcePoints.OrderBy(point => point.Y).ThenBy(point => point.X).ToList();
+
+ if (points.Count == 0)
+ return null;
+
+ int index = useUpperMiddle ? points.Count / 2 : (points.Count - 1) / 2;
+ return points[index];
+ }
+
+ private sealed class PpHolePoint
+ {
+ public PpHolePoint(string text, int x, int y)
+ {
+ Text = text;
+ X = x;
+ Y = y;
+ }
+
+ public string Text { get; }
+ public int X { get; }
+ public int Y { get; }
+ }
+
+ private sealed class PpGenerationRule
+ {
+ public PpGenerationRule(string toolDefinition, List locations)
+ {
+ ToolDefinition = toolDefinition;
+ Locations = locations;
+ }
+
+ public string ToolDefinition { get; }
+ public List Locations { get; }
}
private string GetPpDrillTapeOutputPath()