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()