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);
}
}
}