using System; using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; namespace DrillTools.Integration { /// /// Excellon 钻带涨缩引擎 /// 对钻孔坐标进行 X/Y 方向独立缩放,保持槽孔长度不变 /// internal static class ExcellonScaler { // 坐标模式:X 后跟可选符号的数字(可能含小数点),Y 同理 private static readonly Regex CoordinatePattern = new( @"^X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)$", RegexOptions.Compiled); // G85 槽孔模式:X...Y...G85X...Y... private static readonly Regex SlotPattern = new( @"^(X[+-]?\d+\.?\d*Y[+-]?\d+\.?\d*)G85(X[+-]?\d+\.?\d*Y[+-]?\d+\.?\d*)$", RegexOptions.Compiled); // 解析 X 或 Y 坐标值的子模式 private static readonly Regex XValuePattern = new( @"X([+-]?\d+\.?\d*)", RegexOptions.Compiled); private static readonly Regex YValuePattern = new( @"Y([+-]?\d+\.?\d*)", RegexOptions.Compiled); // 刀具定义行模式(在头部,如 T01C0.799H05000Z+0.000S060.00) private static readonly Regex ToolDefinitionPattern = new( @"^T\d+C", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// 对钻带内容执行涨缩 /// /// 原始钻带内容 /// X 方向缩放倍率 /// Y 方向缩放倍率 /// 缩放后的钻带内容 public static string Scale(string content, double kx, double ky) { if (content == null) throw new ArgumentNullException(nameof(content)); var lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); var result = new List(lines.Length); foreach (var line in lines) { string trimmed = line.Trim(); // 空行直接保留 if (string.IsNullOrEmpty(trimmed)) { result.Add(line); continue; } // 刀具定义行(TxxC...):严格原样保留 if (ToolDefinitionPattern.IsMatch(trimmed)) { result.Add(line); continue; } // G85 槽孔行:缩放中心点,保持槽长不变 var slotMatch = SlotPattern.Match(trimmed); if (slotMatch.Success) { result.Add(ScaleSlotLine(trimmed, kx, ky)); continue; } // 普通坐标行:X...Y... var coordMatch = CoordinatePattern.Match(trimmed); if (coordMatch.Success) { result.Add(ScaleCoordinateLine(trimmed, kx, ky)); continue; } // 其余行原样保留(M48, %, Txx, M30 等) result.Add(line); } return string.Join("\r\n", result); } /// /// 缩放普通坐标行 /// private static string ScaleCoordinateLine(string line, double kx, double ky) { var xMatch = XValuePattern.Match(line); var yMatch = YValuePattern.Match(line); if (!xMatch.Success || !yMatch.Success) return line; double xOrig = double.Parse(xMatch.Groups[1].Value); double yOrig = double.Parse(yMatch.Groups[1].Value); long xNew = (long)Math.Round(xOrig * kx); long yNew = (long)Math.Round(yOrig * ky); return $"X{FormatCoordinateValue(xNew)}Y{FormatCoordinateValue(yNew)}"; } /// /// 缩放 G85 槽孔行 /// 缩放中心点,保持原始矢量恢复起止点(槽长不变) /// private static string ScaleSlotLine(string line, double kx, double ky) { var slotMatch = SlotPattern.Match(line); if (!slotMatch.Success) return line; // 解析起点坐标 var startPart = slotMatch.Groups[1].Value; var xStartMatch = XValuePattern.Match(startPart); var yStartMatch = YValuePattern.Match(startPart); double xStart = double.Parse(xStartMatch.Groups[1].Value); double yStart = double.Parse(yStartMatch.Groups[1].Value); // 解析终点坐标 var endPart = slotMatch.Groups[2].Value; var xEndMatch = XValuePattern.Match(endPart); var yEndMatch = YValuePattern.Match(endPart); double xEnd = double.Parse(xEndMatch.Groups[1].Value); double yEnd = double.Parse(yEndMatch.Groups[1].Value); // 计算中心点 double cx = (xStart + xEnd) / 2.0; double cy = (yStart + yEnd) / 2.0; // 缩放中心点 long cxNew = (long)Math.Round(cx * kx); long cyNew = (long)Math.Round(cy * ky); // 保持原始矢量(槽长不变) long dx = (long)Math.Round((xEnd - xStart) / 2.0); long dy = (long)Math.Round((yEnd - yStart) / 2.0); // 恢复起止点 long newStartX = cxNew - dx; long newStartY = cyNew - dy; long newEndX = cxNew + dx; long newEndY = cyNew + dy; return $"X{FormatCoordinateValue(newStartX)}Y{FormatCoordinateValue(newStartY)}" + $"G85X{FormatCoordinateValue(newEndX)}Y{FormatCoordinateValue(newEndY)}"; } /// /// 将坐标数值格式化为钻带使用的 6 位坐标文本,负数保留负号。 /// private static string FormatCoordinateValue(long value) { if (value < 0) return "-" + Math.Abs(value).ToString("D6", CultureInfo.InvariantCulture); return value.ToString("D6", CultureInfo.InvariantCulture); } } }