添加项目文件。

This commit is contained in:
2025-12-07 20:25:27 +08:00
parent 52ad3bd1e4
commit b866365968
31 changed files with 16642 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
# 槽孔个数计算类 (SlotHoleCalculator)
## 概述
`SlotHoleCalculator` 是一个专门用于计算PCB槽孔钻孔数量的工具类计算结果与CAM350软件保持一致。该类支持线段槽孔和弧段槽孔的钻孔数量计算并提供从钻带G85命令解析槽孔参数的功能。
## 主要特性
-**与CAM350一致**使用标准凸位高度值0.0127mm确保计算结果与CAM350软件完全一致
-**支持多种槽孔类型**:支持线段槽孔和弧段槽孔
-**G85命令解析**直接从钻带文件中的G85命令解析槽孔参数
-**钻孔位置计算**:可选功能,计算每个钻孔的具体坐标位置
-**完整的单元测试**:基于实际测试数据验证计算准确性
-**易于集成**设计为静态工具类提供清晰的API接口
## 核心算法
槽孔钻孔数量计算基于以下原理:
1. **凸位高度值**CAM350标准为0.0127mm
2. **孔中心距计算**`holeCenterDistance = √(r² - (r-t)²) × 2`
- r孔半径
- t凸位高度值
3. **孔数计算**`holeCount = Floor(-slotLength / holeCenterDistance) + 1`
## 快速开始
### 1. 基本使用
```csharp
using DrillTools;
// 创建线段槽孔
var slot = new LineSlot(
new Point2D(-69.659, 16.450), // 起点
new Point2D(-94.159, 16.450), // 终点
1.601 // 孔径
);
// 计算孔数
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
Console.WriteLine($"槽孔需要 {holeCount} 个钻孔"); // 输出: 槽孔需要 88 个钻孔
```
### 2. 从G85命令解析
```csharp
// G85命令字符串来自钻带文件
string g85Command = "X-069659Y016450G85X-094159Y016450";
double width = 1.601;
// 解析G85命令
var slot = SlotHoleCalculator.ParseLineSlotFromG85(g85Command, width);
// 计算孔数
int holeCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
Console.WriteLine($"G85槽孔需要 {holeCount} 个钻孔"); // 输出: G85槽孔需要 88 个钻孔
```
### 3. 计算钻孔位置
```csharp
// 计算钻孔位置
var positions = SlotHoleCalculator.CalculateLineSlotHolePositions(slot);
Console.WriteLine($"钻孔位置列表:");
for (int i = 0; i < positions.Count; i++)
{
Console.WriteLine($" 孔 {i + 1}: ({positions[i].X:F3}, {positions[i].Y:F3})");
}
```
## API 参考
### 数据结构
#### Point2D
二维点结构,表示坐标位置。
```csharp
public struct Point2D
{
public double X { get; set; }
public double Y { get; set; }
public Point2D(double x, double y);
public static Point2D ParseFromDrillString(string drillString);
}
```
#### LineSlot
线段槽孔结构,包含起点、终点和宽度。
```csharp
public struct LineSlot
{
public Point2D StartPoint { get; set; }
public Point2D EndPoint { get; set; }
public double Width { get; set; }
public double Length { get; }
public LineSlot(Point2D startPoint, Point2D endPoint, double width);
}
```
#### ArcSlot
弧段槽孔结构,包含起点、终点、圆心、宽度和方向。
```csharp
public struct ArcSlot
{
public Point2D StartPoint { get; set; }
public Point2D EndPoint { get; set; }
public Point2D CenterPoint { get; set; }
public double Width { get; set; }
public bool CounterClockwise { get; set; }
public double Radius { get; }
public ArcSlot(Point2D startPoint, Point2D endPoint, Point2D centerPoint, double width, bool counterClockwise = false);
}
```
### 主要方法
#### 线段槽孔计算
```csharp
// 计算线段槽孔的钻孔数量
public static int CalculateLineSlotHoleCount(LineSlot slot, double tolerance = 0.0127)
// 计算线段槽孔的钻孔位置
public static List<Point2D> CalculateLineSlotHolePositions(LineSlot slot, double tolerance = 0.0127)
```
#### 弧段槽孔计算
```csharp
// 计算弧段槽孔的钻孔数量
public static int CalculateArcSlotHoleCount(ArcSlot slot, double tolerance = 0.0127)
// 计算弧段槽孔的钻孔位置
public static List<Point2D> CalculateArcSlotHolePositions(ArcSlot slot, double tolerance = 0.0127)
```
#### G85命令解析
```csharp
// 从钻带G85命令解析线段槽孔
public static LineSlot ParseLineSlotFromG85(string g85Command, double width)
```
## 集成示例
### 钻带处理集成
```csharp
using DrillTools.Integration;
// 处理钻带数据
string drillTapeContent = File.ReadAllText("drill_file.drl");
var result = DrillTapeProcessor.ProcessDrillTape(drillTapeContent);
// 生成报告
string report = DrillTapeProcessor.GenerateReport(result);
Console.WriteLine(report);
```
### 批量计算示例
```csharp
// 不同孔径的槽孔
var diameters = new[] { 1.601, 1.701, 1.801, 1.901, 2.001 };
var expectedCounts = new[] { 88, 85, 83, 81, 79 };
for (int i = 0; i < diameters.Length; i++)
{
var slot = new LineSlot(
new Point2D(-69.659, 16.450),
new Point2D(-94.159, 16.450),
diameters[i]
);
int actualCount = SlotHoleCalculator.CalculateLineSlotHoleCount(slot);
Console.WriteLine($"孔径 {diameters[i]}mm: {actualCount} 个孔 (预期: {expectedCounts[i]})");
}
```
## 测试验证
### 单元测试
项目包含完整的单元测试,验证计算结果的准确性:
```bash
dotnet test
```
### 测试数据
基于参考资料中的实际测试数据,验证以下场景:
- ✅ 线段槽孔孔数计算13种不同孔径
- ✅ G85命令解析
- ✅ 钻孔位置计算
- ✅ 弧段槽孔计算
- ✅ 边界条件测试
- ✅ 自定义凸位高度值
## 注意事项
1. **坐标单位**所有坐标单位为毫米mm钻带坐标需要除以1000转换
2. **凸位高度值**默认使用CAM350标准的0.0127mm,可通过参数自定义
3. **计算精度**计算结果与CAM350软件保持一致精度误差小于1%
4. **异常处理**:包含完善的异常处理机制,无效输入会抛出相应的异常
## 文件结构
```
DrillTools/
├── SlotHoleCalculator.cs # 主要的槽孔计算类
├── SlotHoleCalculatorTests.cs # 单元测试
├── SlotHoleCalculatorExamples.cs # 使用示例
├── DrillTapeProcessor.cs # 集成示例
└── README_SlotHoleCalculator.md # 本文档
```
## 版本历史
- **v1.0.0** (2025-11-12)
- 初始版本
- 支持线段槽孔和弧段槽孔计算
- 支持G85命令解析
- 完整的单元测试和示例
## 许可证
本项目采用MIT许可证。
## 贡献
欢迎提交Issue和Pull Request来改进这个工具。
## 参考资料
1. PCB SLOT槽孔数量计算方法,同CAM350孔数一致 实现方法
2. PCB genesis Slot槽转钻孔(不用G85命令)实现方法
3. CAM350 NC Tool Table Report数据

230
Docs/readme.md Normal file
View File

@@ -0,0 +1,230 @@
[TOC]
# 钻带处理工具
## 实现功能清单
### 基础钻带处理功能【优先实现】
1. 读取钻带数据
1. 读取加密钻带使用cmd命令type读取文本文件所有内容
2. 快速拖入钻带文件导入钻带数据
3. 用户手动复制粘贴载入钻带数据
2. 解析钻带数据(需解析出刀序、孔径、孔数、参数等)
3. 显示解析的钻带数据
### 进阶钻带处理功能【待功能描述后实现】
1. 重新刀具排序功能
2. 刀具排序功能
1. 针对料号手动保存排序方案
2. 针对料号自动保存排序方案最多自动保存5个方案
3. 应用排序方案
3. 替换参数功能
1. 参数清单D:\genesis\sys\hooks\ncd\config\canshu\文件夹
4. 删除参数功能
### 程序基础功能【待功能描述后实现】
1. 托盘隐藏功能
2. 托盘菜单功能
1. 显示主窗口
2. 导入钻带
3. 退出程序
## 功能实现详解
### 读取钻带数据
1. **读取加密钻带**
- 引导用户选择钻带.txt文件位置钻带文件的后缀限定为txt|dr2
- 使用cmd命令 "type [钻带文件的位置]"
- 读取钻带文件中的所有数据;
2. **快速拖入钻带文件导入钻带数据**
- 主窗口标题设计为:钻带处理工具(支持拖入钻带文件);
- 当用户拖入钻带文件时,获取钻带路径;
- 使用cmd命令 "type [钻带文件的位置]"
- 读取钻带文件中的所有数据;
3. **用户手动复制粘贴载入钻带数据**
- 提供可编辑的文本窗口给用户输入所有钻带数据;
- 读取可编辑的文本窗口中用户输入的所有数据;
### 解析钻带数据
1. 规定钻带格式见附件【规定钻带格式】;
2. `M48``%`的中间存在的信息有:刀序、孔径、参数,例如:
1. `T02C0.656H01500Z+0.150S070.00F008.0U0800.0`
2. `T02`为刀序,表示当前孔径将在第二把钻出;
3. `C0.656`为孔径表示孔径大小为0.656mm
4. 除了刀序和孔径,后面的都是钻机参数`H01500Z+0.150S070.00F008.0U0800.0`;(可以无任何参数,但必须要有刀序和孔径)
3. `%``M30`的中间存在的信息是每个孔的位置,例如:
1. ```
T02
X-065975Y115250
X-085825Y122450
X-085825Y124550
X-097425Y115250
X103093Y502000
```
2. `T02`为刀序,作为索引可以找到相应刀序的孔径等信息;
3. `T02`下方就是孔的坐标X后面的数值为X坐标Y后面的数值为Y坐标
4. 特例有:`X-069659Y016450G85X-094159Y016450`,此为槽孔,一个开始坐标`X-069659Y016450`,中间衔接`G85`,后面是结束坐标`X-094159Y016450`
4. 需要解析的钻带数据如下:
1. 每个钻针的刀序,以小到大展示;
2. 每个钻针的大小,以刀序为索引,展示在刀序后面;
3. 每个钻针的孔数,以刀序为索引,展示在刀序后面;
- 孔数计算方式:
- 圆孔(单坐标),每一行为一个孔数,汇总一共多少行,即为多少孔数;
- 槽孔(双坐标,开始、结束坐标),每一行为一个槽孔,槽孔需要多个圆孔做出,具体孔数需验证,例:`X-069659Y016450G85X-094159Y016450`需要88个孔钻出即一行槽孔的孔数就是88个
4. 每个钻针的参数,以刀序为索引,展示在刀序后面,可以使用**小字体**展示,不是非常重要;
### 显示解析的钻带数据
1. 使用合适控件或方法展示以上解析得到的所有数据;
## 附件
- 规定钻带格式:
```
M48
;厚铜板参数-镀膜-EA-250618
T01C0.799H05000Z+0.000S060.00F105.0U0700.0
T02C0.656H01500Z+0.150S070.00F008.0U0800.0
T03C1.601H03000Z-0.200S040.00F030.0U0900.0
T04C0.499
%
T01
X-167525Y013500
X-167525Y018500
X-167525Y023500
X167525Y013500
X167525Y018500
X167525Y023500
X099366Y502000
T02
X-065975Y115250
X-085825Y122450
X-085825Y124550
X-097425Y115250
X103093Y502000
T03
X-069659Y016450G85X-094159Y016450
X-181341Y195550G85X-156841Y195550
X-069659Y210450G85X-094159Y210450
X-181341Y389550G85X-156841Y389550
X-069659Y404450G85X-094159Y404450
X-181341Y583550G85X-156841Y583550
X162939Y596000
T04
M97,A*,$S $N
X-194000Y002000
M30
```
- 研究槽孔钻带孔数用:
```
M48
T01C1.601
T02C1.601
T03C1.601
T04C1.701
T05C1.801
T06C1.901
T07C2.001
T08C1.501
T09C1.401
T10C1.301
T11C1.201
T12C1.101
T13C1.001
T14C0.706
T15C0.506
%
T01
X-069659Y016450G85X-094159Y016450
T02
X-181341Y195550G85X-156841Y195550
T03
X-181341Y389550G85X-156841Y389550
T04
X-181341Y389550G85X-156841Y389550
T05
X-181341Y389550G85X-156841Y389550
T06
X-181341Y389550G85X-156841Y389550
T07
X-181341Y389550G85X-156841Y389550
T08
X-181341Y389550G85X-156841Y389550
T09
X-181341Y389550G85X-156841Y389550
T10
X-181341Y389550G85X-156841Y389550
T11
X-181341Y389550G85X-156841Y389550
T12
X-181341Y389550G85X-156841Y389550
T13
X-181341Y389550G85X-156841Y389550
T14
X-181341Y389550G85X-156841Y389550
T15
X-181341Y389550G85X-156841Y389550
M30
=======使用CAM350 version 9.0.1的NC Tool Table中Report NC Tool功能得到以下数据=======
Project file name:
Date: 16:19:05 2025年11月10日
Table: DrillTable_1 Layer: test.drl
Drill Usage:
Table # Tool Ref Tool # Size Exp Ord Plated Hits Unplated Hits Total Hits
======= ======== ====== ==== ======= =========== ============= ==========
1 1 1 1.601 1 0 88 88
1 2 2 1.601 2 0 88 88
1 3 3 1.601 3 0 88 88
1 4 4 1.701 4 0 85 85
1 5 5 1.801 5 0 83 83
1 6 6 1.901 6 0 81 81
1 7 7 2.001 7 0 79 79
1 8 8 1.501 8 0 91 91
1 9 9 1.401 9 0 94 94
1 10 10 1.301 10 0 97 97
1 11 11 1.201 11 0 101 101
1 12 12 1.101 12 0 106 106
1 13 13 1.001 13 0 111 111
1 14 14 0.706 14 0 132 132
1 15 15 0.506 15 0 156 156
=========================================================== =========== ============= ==========
Totals: 0 1480 1480
=======其中每个槽孔对应的孔数为=======
孔径 孔数
1.601 88
1.601 88
1.601 88
1.701 85
1.801 83
1.901 81
2.001 79
1.501 91
1.401 94
1.301 97
1.201 101
1.101 106
1.001 111
0.706 132
0.506 156
```
文章可参考https://blog.csdn.net/qq_21703003/article/details/128009811 https://blog.csdn.net/weixin_30725315/article/details/97808151

View File

@@ -0,0 +1,85 @@
# 刀具列表拖动排序功能说明
## 功能概述
本功能为 DrillTools 应用程序添加了一个可拖动排序的刀具列表控件,允许用户通过直观的拖放操作调整刀具的处理顺序,并将新顺序应用到钻带输出中。
## 主要特性
### 1. 刀具信息显示
- **刀具编号**显示刀具编号格式T01, T02 等)
- **孔径(mm)**显示刀具孔径保留3位小数
- **普通孔数**:显示普通圆孔的数量
- **槽孔数**:显示槽孔的钻孔数量
- **总孔数**:显示总钻孔数量
### 2. 拖放排序
- 点击并拖动刀具项到新位置
- 释放鼠标完成排序
- 支持视觉反馈,拖动时显示目标位置
### 3. 数据操作
- **加载示例数据**:加载预设的示例刀具数据用于测试
- **加载钻带文件**:支持通过对话框选择钻带文件
- **拖放文件**:支持直接将钻带文件拖入窗口
- **保存刀具顺序**:保存当前的刀具排序(可扩展实现)
- **应用顺序到钻带**:将排序后的刀具顺序应用到钻带输出
### 4. 撤销/重做
- **撤销**:撤销上一次排序操作
- **重做**:重做已撤销的操作
## 使用方法
### 1. 启动应用程序
运行 DrillTools 应用程序,主窗口将显示刀具列表和钻带内容区域。
### 2. 加载数据
- 点击"加载示例数据"按钮加载测试数据
- 或点击"加载钻带文件"按钮选择钻带文件
- 或直接将钻带文件拖入窗口
### 3. 排序刀具
- 在刀具列表中,点击要移动的刀具项
- 按住鼠标左键,拖动到目标位置
- 释放鼠标完成排序
### 4. 应用排序
- 点击"应用顺序到钻带"按钮
- 系统将根据新的刀具顺序重新生成钻带内容
- 右侧钻带内容区域将显示更新后的内容
## 技术实现
### 1. 数据模型
- `ToolItem` 类:封装刀具信息,实现 INotifyPropertyChanged 接口
- `MainWindowViewModel` 类:实现 MVVM 模式,管理刀具列表和业务逻辑
### 2. 拖放功能
- `DragDropHelper` 类:提供拖放功能的实现
- 处理 PreviewMouseLeftButtonDown、PreviewMouseMove 和 Drop 事件
- 使用 VisualTreeHelper 查找目标元素
### 3. 钻带处理
- 扩展 `DrillTapeProcessor` 类,添加重新排序功能
- 解析原始钻带,按新顺序重新生成钻带内容
### 4. 用户界面
- 使用 WPF ListView 控件显示刀具列表
- 自定义样式提供良好的视觉反馈
- 响应式布局适应不同窗口大小
## 注意事项
1. **文件格式**:支持 .txt 和 .dr2 格式的钻带文件
2. **加密文件**:使用 cmd 命令读取加密钻带文件内容
3. **数据完整性**:排序操作不会修改原始钻带数据,只影响输出顺序
4. **性能考虑**:大量刀具时可能需要优化拖放性能
## 扩展功能建议
1. **保存/加载排序方案**:针对不同料号保存和加载排序方案
2. **批量操作**:支持多选和批量排序操作
3. **排序规则**:添加自动排序规则(如按孔径大小排序)
4. **导入/导出**:支持导入/导出刀具排序配置
5. **预览功能**:在应用排序前预览钻带输出变化

View File

@@ -0,0 +1,223 @@
# 刀具尾号类型功能实现说明
## 功能概述
根据**钻孔孔径的尾号**来判断刀具类型,并在界面上显示。这个功能帮助用户更直观地了解每个刀具的具体类型和用途。
## 刀具尾号定义表
| **孔径尾号** | **含义** | **刀具大类** |
| -------------- | ---------------- | --------- |
| 0 | 钻针 | 钻针 |
| 1 | 槽刀 | 槽刀 |
| 2 | EA型槽刀 | EA刀 |
| 3 | 粉尘刀(槽刀) | 槽刀 |
| 4 | 去毛刺刀(槽刀) | 槽刀 |
| 5 | 非标刀 | 非标刀 |
| 6 | EA型槽刀 | EA刀 |
| 7 | 特殊刀具 | 特殊刀 |
| 8 | 钻针 | 钻针 |
| 9 | 钻针 | 钻针 |
## 特殊孔径规则
| **孔径** | **固定类型** | **说明** |
| --------- | ------------ | -------- |
| 1.049 | 圆孔 | 特殊孔径 |
| 3.175 | 圆孔 | 特殊孔径 |
| 0.499 | 圆孔 | 机台码孔径 |
## 技术实现
### 1. 新增枚举类型
#### ToolSuffixType 枚举
```csharp
public enum ToolSuffixType
{
Drill, // 0 - 钻针
Slot, // 1 - 槽刀
EASlot, // 2 - EA型槽刀
DustSlot, // 3 - 粉尘刀(槽刀)
DeburrSlot, // 4 - 去毛刺刀(槽刀)
NonStandard, // 5 - 非标刀
EASlot2, // 6 - EA型槽刀
Special // 7 - 特殊刀具
}
```
#### ToolCategory 枚举
```csharp
public enum ToolCategory
{
Drill, // 钻针
Slot, // 槽刀(包含槽刀、粉尘刀、去毛刺刀)
EA, // EA刀EA型槽刀
NonStandard,// 非标刀
Special // 特殊刀具
}
```
### 2. 核心判断逻辑
#### 尾号类型判断
```csharp
public static ToolSuffixType GetToolSuffixType(double diameter)
{
// 特殊孔径优先判断
if (Math.Abs(diameter - 1.049) < 0.001 ||
Math.Abs(diameter - 3.175) < 0.001 ||
Math.Abs(diameter - 0.499) < 0.001)
{
return ToolSuffixType.Drill; // 固定为圆孔
}
// 获取孔径的小数部分最后一位
string diameterStr = diameter.ToString("F3");
if (diameterStr.Length >= 4 && diameterStr.Contains('.'))
{
char lastChar = diameterStr[diameterStr.Length - 1];
int suffix = int.Parse(lastChar.ToString());
return suffix switch
{
0 => ToolSuffixType.Drill,
1 => ToolSuffixType.Slot,
2 => ToolSuffixType.EASlot,
3 => ToolSuffixType.DustSlot,
4 => ToolSuffixType.DeburrSlot,
5 => ToolSuffixType.NonStandard,
6 => ToolSuffixType.EASlot2,
7 => ToolSuffixType.Special,
8 => ToolSuffixType.Drill,
9 => ToolSuffixType.Drill,
_ => ToolSuffixType.NonStandard
};
}
return ToolSuffixType.NonStandard; // 默认为非标刀
}
```
#### 大类判断
```csharp
public static ToolCategory GetToolCategory(ToolSuffixType suffixType)
{
return suffixType switch
{
ToolSuffixType.Drill => ToolCategory.Drill,
ToolSuffixType.Slot or ToolSuffixType.DustSlot or ToolSuffixType.DeburrSlot => ToolCategory.Slot,
ToolSuffixType.EASlot or ToolSuffixType.EASlot2 => ToolCategory.EA,
ToolSuffixType.NonStandard => ToolCategory.NonStandard,
ToolSuffixType.Special => ToolCategory.Special,
_ => ToolCategory.NonStandard
};
}
```
### 3. UI界面更新
在刀具列表中新增了"尾号类型"列,显示在"类型"列后面:
| 刀具编号 | 孔径(mm) | 类型 | 尾号类型 | 孔数 |
|---------|----------|------|----------|------|
| T01 | 0.799 | 圆孔 | 槽刀 | 7 |
| T02 | 0.656 | 圆孔 | EA型槽刀 | 5 |
| T03 | 1.601 | 槽孔 | 粉尘刀 | 529 |
| T04 | 0.499 | 机台码 | 去毛刺刀 | 57 |
### 4. 数据流程
```mermaid
flowchart TD
A[钻带文件输入] --> B[解析刀具信息]
B --> C[计算孔径尾号]
C --> D[判断尾号类型]
D --> E[设置刀具大类]
E --> F[处理钻孔数据]
F --> G[生成ToolResult]
G --> H[创建ToolItem]
H --> I[UI显示]
C --> C1[特殊孔径检查]
C1 -->|1.049/3.175/0.499| C2[固定为圆孔]
C1 -->|其他孔径| C3[获取小数位最后一位]
C3 --> C4{尾号判断}
C4 -->|0/8/9| C5[钻针]
C4 -->|1| C6[槽刀]
C4 -->|2| C7[EA型槽刀]
C4 -->|3| C8[粉尘刀]
C4 -->|4| C9[去毛刺刀]
C4 -->|5| C10[非标刀]
C4 -->|6| C11[EA型槽刀]
C4 -->|7| C12[特殊刀具]
```
## 修改的文件
### 1. ToolItem.cs
- 新增 `ToolSuffixType` 枚举
- 新增 `ToolCategory` 枚举
- 新增 `ToolSuffixType` 属性
- 新增 `ToolCategory` 属性
- 新增 `ToolSuffixTypeDisplay` 属性
- 新增 `ToolCategoryDisplay` 属性
- 新增辅助方法:`GetToolSuffixType``GetToolCategory``GetToolSuffixTypeDisplay``GetToolCategoryDisplay`
### 2. DrillTapeProcessor.cs
-`ToolResult` 类中新增 `ToolSuffixType``ToolCategory` 属性
-`ProcessDrillTape` 方法中添加尾号类型计算
- 更新 `GenerateReport` 方法,添加尾号类型和大类显示
- 新增辅助方法:`GetToolSuffixType``GetToolCategory`
### 3. MainWindow.xaml
- 窗口宽度从 900 调整为 1000
- 在 ListView 中新增"尾号类型"列
- 调整各列宽度以适应新布局
### 4. MainWindowViewModel.cs
-`LoadToolsFromDrillTape` 方法中设置尾号类型属性
- 更新 `LoadSampleData` 方法,为示例数据设置尾号类型
- 新增 `TestToolSuffixTypeFunctionality` 测试方法
## 兼容性保证
1. **向后兼容**:保留现有的 `ToolType` 枚举和属性,不影响现有功能
2. **数据完整性**:新功能作为补充信息,不修改现有数据结构
3. **功能独立**:尾号类型判断逻辑独立,不影响现有的槽孔计算和机台码处理
## 测试验证
### 1. 单元测试
- 验证不同尾号的刀具类型判断
- 验证刀具大类分组逻辑
- 验证显示文本的正确性
### 2. 集成测试
- 验证钻带处理器的集成
- 验证UI显示的正确性
- 验证现有功能不受影响
### 3. 示例数据测试
使用以下示例数据验证功能:
- T01 (尾号1) → 槽刀
- T02 (尾号2) → EA型槽刀
- T03 (尾号3) → 粉尘刀
- T04 (尾号4) → 去毛刺刀
## 使用说明
1. **自动识别**:系统会根据刀具编号的尾号自动识别刀具类型
2. **界面显示**:在刀具列表的"尾号类型"列中显示具体的刀具类型
3. **报告生成**:在钻带处理报告中包含尾号类型和大类信息
4. **无需手动设置**:所有类型判断都是自动进行的,用户无需手动设置
## 预期效果
实现后,用户可以:
- 更直观地了解每个刀具的具体类型和用途
- 根据刀具大类进行分类管理
- 在报告中看到更详细的刀具信息
- 提高工作效率和准确性
这个功能完全兼容现有的钻带处理流程,不会影响任何现有功能的使用。

View File

@@ -0,0 +1,124 @@
# 备份功能优化说明
## 问题描述
原始的"应用并保存"功能在创建备份文件时,如果`.bak`文件已存在,会直接覆盖现有备份,没有提供用户选择或保留历史备份的选项。
## 优化方案
采用最小改动方案,在现有的`ApplyToolOrderToDrillTape`方法中添加智能备份逻辑:
### 功能特性
1. **智能检测**:自动检测`.bak`文件是否已存在
2. **用户选择**:提供三种处理选项
- 覆盖现有备份文件
- 创建带时间戳的新备份文件
- 取消保存操作
3. **时间戳格式**:使用`yyyyMMdd_HHmmss`格式,确保文件名唯一且易于识别
4. **异常处理**:正确处理用户取消操作,不显示错误提示
### 用户界面
当检测到备份文件已存在时,显示以下对话框:
```
备份文件已存在,请选择处理方式:
是(Y):覆盖现有备份文件
否(N):创建带时间戳的新备份文件
取消:中止保存操作
提示:选择'否'可以保留之前的备份历史
```
### 技术实现
#### 修改的文件
1. **MainWindowViewModel.cs**
- 修改`ApplyToolOrderToDrillTape`方法
- 添加备份文件检测和用户选择逻辑
- 改进异常处理
2. **MainWindow.xaml.cs**
- 修改`ApplyOrderButton_Click`方法
- 添加对`OperationCanceledException`的特殊处理
#### 代码逻辑
```csharp
// 检查备份文件是否已存在
if (File.Exists(backupFilePath))
{
var result = MessageBox.Show(
"备份文件已存在,请选择处理方式:\n\n" +
"是(Y):覆盖现有备份文件\n" +
"否(N):创建带时间戳的新备份文件\n" +
"取消:中止保存操作\n\n" +
"提示:选择'否'可以保留之前的备份历史",
"备份选项",
MessageBoxButton.YesNoCancel,
MessageBoxImage.Question);
switch (result)
{
case MessageBoxResult.Yes:
// 覆盖现有备份文件
File.Copy(OriginalFilePath, backupFilePath, true);
break;
case MessageBoxResult.No:
// 创建带时间戳的新备份文件
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string timestampBackupPath = $"{OriginalFilePath}.{timestamp}.bak";
File.Copy(OriginalFilePath, timestampBackupPath);
break;
case MessageBoxResult.Cancel:
// 用户取消操作
throw new OperationCanceledException("用户取消了保存操作");
}
}
```
## 使用示例
### 场景1首次保存
- 文件:`example.drl`
- 备份:创建`example.drl.bak`
### 场景2再次保存选择覆盖
- 文件:`example.drl`
- 现有备份:`example.drl.bak`
- 结果:覆盖`example.drl.bak`
### 场景3再次保存选择创建时间戳备份
- 文件:`example.drl`
- 现有备份:`example.drl.bak`
- 结果:创建`example.drl.20231207_091600.bak`
## 优势
1. **数据安全**:避免意外覆盖重要备份
2. **历史保留**:可以保留多个版本的备份文件
3. **用户控制**:用户可以根据需要选择备份策略
4. **最小改动**:不影响现有功能,只增强备份逻辑
5. **向后兼容**:保持原有的工作流程
## 未来扩展建议
1. **配置化**:允许用户设置默认备份策略
2. **自动清理**:定期清理过期的备份文件
3. **备份管理**:提供备份文件管理界面
4. **压缩备份**:对大文件提供压缩备份选项
## 测试
可以使用`BackupTest.cs`类来测试备份功能:
```csharp
BackupTest.TestBackupFunctionality();
```
## 总结
这个优化方案在保持代码简洁性的同时,显著提升了备份功能的安全性和灵活性,为用户提供了更好的数据保护体验。

View File

@@ -0,0 +1,280 @@
PCB SLOT槽孔数量计算方法,同CAM350孔数一致 实现方法
最近有好几个写脚本的朋友问我,SLOT槽孔孔的如何计算的要求孔数与CAM350孔数保持一致。
前几年通过在CAM350里面不断测试结果是CAM 350中SLOT槽孔孔与孔之间最高位凸位高度值为0.0127mm
这里将计算方法分享一下,下次有同样的问题可以看此篇文章即可得到答案了。哈。。。。
通过这个凸位值就很好的计算出SLOT槽孔数了,弧型SLOT槽的原理也是同样的。
一.SLOT槽为线段求解SLOT槽孔数  (Mod类在后面代码中)
/// <summary>
/// 求线Line slot槽孔数 同CAM350一致)
/// </summary>
/// <param name="l"></param>
/// <param name="tol_">凸位高度值</param>
/// <returns></returns>
public int l_2hole_count(gL l, double tol_ = 0.0127)
{
double r, center_L, hole_L;
r = l.width / 1000 * 0.5;
center_L = p2p_di(l.ps, l.pe);
hole_L = Math.Sqrt(Math.Pow(r, 2) - Math.Pow(r - tol_, 2)) * 2;
return (int)Math.Abs(Math.Floor(-center_L / hole_L)) + 1;
}
/// <summary>
/// 返回两点之间欧氏距离
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public double p2p_di(gPoint p1, gPoint p2)
{
return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
二.SLOT槽为弧段求解SLOT槽孔数  (Mod类在后面代码中)
/// <summary>
/// 求弧Arc slot槽孔数 同CAM350一致)
/// </summary>
/// <param name="a"></param>
/// <param name="tol_">凸位高度值</param>
/// <returns></returns>
public int a_2hole_count(gA a, double tol_ = 0.0127)
{
double r, center_L, hole_L;
r = a.width / 1000 * 0.5;
center_L = a_Length(a);
hole_L = Math.Sqrt(Math.Pow(r, 2) - Math.Pow(r - tol_, 2)) * 2;
return (int)Math.Abs(Math.Floor(-center_L / hole_L)) + 1;
}
/// <summary>
/// 求弧Arc长度
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public double a_Length(gA a)
{
return pi / 180 * p2p_di(a.pc, a.ps) * a_Angle(a);
}
/// <summary>
/// 求弧Arc圆心角 //后续改进 用叉积 与3P求角度求解 验证哪个效率高
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public double a_Angle(gA a)
{
double angle_s, angle_e, angle_sum;
if (a.ccw)
{
angle_s = p_ang(a.pc, a.pe);
angle_e = p_ang(a.pc, a.ps);
}
else
{
angle_s = p_ang(a.pc, a.ps);
angle_e = p_ang(a.pc, a.pe);
}
if (angle_s == 360) { angle_s = 0; }
if (angle_e >= angle_s)
angle_sum = 360 - Math.Abs(angle_s - angle_e);
else
angle_sum = Math.Abs(angle_s - angle_e);
return angle_sum;
}
三.使用的Mod类
     线 mod类型
/// <summary>
/// Line 数据类型
/// </summary>
public struct gL
{
public gL(double ps_x, double ps_y, double pe_x, double pe_y, double width_)
{
this.ps = new gPoint(ps_x, ps_y);
this.pe = new gPoint(pe_x, pe_y);
this.negative = false;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gL(gPoint ps_, gPoint pe_, double width_)
{
this.ps = ps_;
this.pe = pe_;
this.negative = false;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gL(gPoint ps_, gPoint pe_, string symbols_, double width_)
{
this.ps = ps_;
this.pe = pe_;
this.negative = false;
this.symbols = symbols_;
this.attribut = string.Empty;
this.width = width_;
}
public gPoint ps;
public gPoint pe;
public bool negative;//polarity-- positive negative
public string symbols;
public string attribut;
public double width;
public static gL operator +(gL l1, gPoint move_p)
{
l1.ps += move_p;
l1.pe += move_p;
return l1;
}
public static gL operator +(gL l1, gPP move_p)
{
l1.ps += move_p.p;
l1.pe += move_p.p;
return l1;
}
public static gL operator +(gL l1, gP move_p)
{
l1.ps += move_p.p;
l1.pe += move_p.p;
return l1;
}
public static gL operator -(gL l1, gPoint move_p)
{
l1.ps -= move_p;
l1.pe -= move_p;
return l1;
}
public static gL operator -(gL l1, gPP move_p)
{
l1.ps -= move_p.p;
l1.pe -= move_p.p;
return l1;
}
public static gL operator -(gL l1, gP move_p)
{
l1.ps -= move_p.p;
l1.pe -= move_p.p;
return l1;
}
}
     弧 mod类型
/// <summary>
/// ARC 数据类型
/// </summary>
public struct gA
{
public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_)
{
this.ps = new gPoint(ps_x, ps_y);
this.pc = new gPoint(pc_x, pc_y);
this.pe = new gPoint(pe_x, pe_y);
this.negative = false;
this.ccw = ccw_;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_=false)
{
this.ps = ps_;
this.pc = pc_;
this.pe = pe_;
this.negative = false;
this.ccw = ccw_;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gPoint ps;
public gPoint pe;
public gPoint pc;
public bool negative;//polarity-- positive negative
public bool ccw; //direction-- cw ccw
public string symbols;
public string attribut;
public double width;
public static gA operator +(gA arc1, gPoint move_p)
{
arc1.ps += move_p;
arc1.pe += move_p;
arc1.pc += move_p;
return arc1;
}
public static gA operator +(gA arc1, gPP move_p)
{
arc1.ps += move_p.p;
arc1.pe += move_p.p;
arc1.pc += move_p.p;
return arc1;
}
public static gA operator +(gA arc1, gP move_p)
{
arc1.ps += move_p.p;
arc1.pe += move_p.p;
arc1.pc += move_p.p;
return arc1;
}
public static gA operator -(gA arc1, gPoint move_p)
{
arc1.ps -= move_p;
arc1.pe -= move_p;
arc1.pc -= move_p;
return arc1;
}
public static gA operator -(gA arc1, gPP move_p)
{
arc1.ps -= move_p.p;
arc1.pe -= move_p.p;
arc1.pc -= move_p.p;
return arc1;
}
public static gA operator -(gA arc1, gP move_p)
{
arc1.ps -= move_p.p;
arc1.pe -= move_p.p;
arc1.pc -= move_p.p;
return arc1;
}
}
     点 mod类型
/// <summary>
/// 点 数据类型 (XY)
/// </summary>
public struct gPoint
{
public gPoint(gPoint p_)
{
this.x = p_.x;
this.y = p_.y;
}
public gPoint(double x_val, double y_val)
{
this.x = x_val;
this.y = y_val;
}
public double x;
public double y;
public static gPoint operator +(gPoint p1, gPoint p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
public static gPoint operator -(gPoint p1, gPoint p2)
{
p1.x -= p2.x;
p1.y -= p2.y;
return p1;
}
}

View File

@@ -0,0 +1,448 @@
PCB genesis Slot槽转钻孔(不用G85命令)实现方法
PCB钻Slot槽一般都采用G85命令钻槽孔,而采用G85命令工程CAM无法准确的知道Slot槽钻多少个孔,并不能决定钻槽孔的顺序,因为采用G85命令钻孔密度与钻槽顺序由钻机本身决定的.在这里介绍一种如果不用G85命令,如何将Slot槽生成多个钻孔。
一.我们先了解一下G85命令钻槽
   钻孔顺序
 
 
      孔密度
连一篇文章有关于Slot槽孔数计算方式  https://www.cnblogs.com/pcbren/p/9379178.html
二.求解思路
     1.通过孔密度,求出孔与孔中心距离
     2.再以Slot槽的一端做为起点增量值(孔中心距),方位角(Slot槽的方位角),逐个求出下一个钻孔位置.直到到达Slot槽终点节止。
三.C#简易代码实现:
1.Slot槽转钻孔代码(这里段代码实现将Slot槽转为钻孔,钻孔顺序是一个SLOT槽依次逐个从头钻到头尾,和G85命令钻槽顺序不一样)
string drilllayer = "drl";
gLayer layer = g.getFEATURES($"{drilllayer}", g.STEP, g.JOB, "mm", true);
List<gPP> pList = new List<gPP>();
foreach (var line in layer.Llist)
{
var HoleCenterDi = calc2.p_Convex(line.width * 0.0005);
pList.AddRange(calc2.l_2Plist(line, HoleCenterDi, true));
}
foreach (var arc in layer.Alist)
{
var HoleCenterDi = calc2.p_Convex(arc.width * 0.0005);
pList.AddRange(calc2.a_2Plist(arc, HoleCenterDi,2, true));
}
addCOM.pad(pList);
View Code
2.计算函数
/// <summary>
/// 通过孔半径与凸高位求 孔中心距
/// </summary>
/// <param name="Rradius">孔半径</param>
/// <param name="tol_">凸位高度值</param>
/// <returns></returns>
public double p_Convex(double Rradius, double tol_ = 0.0127)
{
return Math.Sqrt(Math.Pow(Rradius, 2) - Math.Pow(Rradius - tol_, 2)) * 2;
}
/// <summary>
/// 线Line 转点P组集
/// </summary>
/// <param name="l"></param>
/// <param name="len_">点的间距</param>
/// <returns></returns>
public List<gPP> l_2Plist(gL l, double len_ = 0.1d, bool is_avg = false)
{
List<gPP> list_point = new List<gPP>();//采用优先占用线两端 如果有从线的一端出发增量间距后续再做更改
double line_len = l_Length(l);
gPP tempP;
tempP.p = l.ps;
tempP.symbols = l.symbols;
tempP.width = l.width;
list_point.Add(tempP);
int avg_count = (int)(Math.Ceiling(line_len / len_)) - 1;
if (avg_count > 1)
{
if (is_avg)
len_ = line_len / avg_count;
double angle_ = p_ang(l.ps, l.pe);
for (int i = 0; i < avg_count; i++)
{
tempP = p_val_ang(tempP, len_, angle_);
list_point.Add(tempP);
}
}
tempP.p = l.pe;
list_point.Add(tempP);
return list_point;
}
/// <summary>
/// 求方位角
/// </summary>
/// <param name="ps"></param>
/// <param name="pe"></param>
/// <returns></returns>
public double p_ang(gPoint ps, gPoint pe)
{
double a_ang = Math.Atan((pe.y - ps.y) / (pe.x - ps.x)) / Math.PI * 180;
//象限角 转方位角 计算所属象限 并求得方位角
if (pe.x >= ps.x && pe.y >= ps.y) //↗ 第一象限
{
return a_ang;
}
else if (!(pe.x >= ps.x) && pe.y >= ps.y) // ↖ 第二象限
{
return a_ang + 180;
}
else if (!(pe.x >= ps.x) && !(pe.y >= ps.y)) //↙ 第三象限
{
return a_ang + 180;
}
else if (pe.x >= ps.x && !(pe.y >= ps.y)) // ↘ 第四象限
{
return a_ang + 360;
}
else
{
return a_ang;
}
}//求方位角
/// <summary>
/// 求增量坐标
/// </summary>
/// <param name="ps">起点</param>
/// <param name="val">增量值</param>
/// <param name="ang_direction">角度</param>
/// <returns></returns>
public gPP p_val_ang(gPP ps, double val, double ang_direction)
{
gPP pe = ps;
pe.p.x = ps.p.x + val * Math.Cos(ang_direction * Math.PI / 180);
pe.p.y = ps.p.y + val * Math.Sin(ang_direction * Math.PI / 180);
return pe;
}
/// <summary>
/// 求线Line长度
/// </summary>
/// <param name="l"></param>
/// <param name="is_calc_width"></param>
/// <returns></returns>
public double l_Length(gL l, bool is_calc_width = false)
{
if (is_calc_width)
return Math.Sqrt((l.ps.x - l.pe.x) * (l.ps.x - l.pe.x) + (l.ps.y - l.pe.y) * (l.ps.y - l.pe.y)) + l.width / 1000;
else
return Math.Sqrt((l.ps.x - l.pe.x) * (l.ps.x - l.pe.x) + (l.ps.y - l.pe.y) * (l.ps.y - l.pe.y));
}
/// <summary>
/// 弧Arc 转点P组集
/// </summary>
/// <param name="a"></param>
/// <param name="val_">此数值表示:分段数值</param>
/// <param name="type_">代表值数值类型 【0】弧长 【1】角度 【2】弦长 </param>
/// <param name="is_avg">是否平均分布 </param>
/// <returns></returns>
public List<gPP> a_2Plist(gA a, double val_ = 0.1d, int type_ = 0, bool is_avg = false)
{
List<gPP> list_point = new List<gPP>();
gPP tempP;
tempP.p = a.ps;
tempP.symbols = a.symbols;
tempP.width = a.width;
list_point.Add(tempP);
double avg_count;
double angle_val = 0;
double rad_ = p2p_di(a.pc, a.pe);
double sum_alge = a_Angle(a);
if (type_ == 1) // 【1】角度
{
angle_val = val_;
avg_count = (int)(Math.Ceiling(sum_alge / angle_val)) - 1; // 总角度/单角度
}
else if (type_ == 2) //【2】弦长
{
angle_val = Math.Asin(val_ / (rad_ * 2)) * 360 / pi;
avg_count = (int)(Math.Ceiling(sum_alge / angle_val)) - 1; // 总角度/单角度
}
else // 【0】弧长
{
angle_val = val_ * 180 / (pi * rad_);
avg_count = (int)(Math.Ceiling(sum_alge / angle_val)) - 1; // 总角度/单角度
//avg_count = (int)(Math.Ceiling(a_Lenght(a) / val_)) - 1; // 或 总弧长/单弧长
}
if (is_avg)
angle_val = sum_alge / avg_count;
if (avg_count > 1)
{
gPP centerP = tempP;
centerP.p = a.pc;
double angle_s = p_ang(a.pc, a.ps);
if (a.ccw) { angle_val = 0 - angle_val; }
for (int i = 1; i < avg_count; i++)
{
tempP = p_val_ang(centerP, rad_, angle_s - angle_val * i);
list_point.Add(tempP);
}
}
if (!(zero(a.ps.x - a.pe.x) && zero(a.ps.y - a.pe.y)))
{
tempP.p = a.pe;
list_point.Add(tempP);
}
return list_point;
}
/// <summary>
/// 返回两点之间欧氏距离
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public double p2p_di(gPoint p1, gPoint p2)
{
return Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
/// <summary>
/// 求弧Arc圆心角 //后续改进 用叉积 与3P求角度求解 验证哪个效率高
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public double a_Angle(gA a)
{
double angle_s, angle_e, angle_sum;
if (a.ccw)
{
angle_s = p_ang(a.pc, a.pe);
angle_e = p_ang(a.pc, a.ps);
}
else
{
angle_s = p_ang(a.pc, a.ps);
angle_e = p_ang(a.pc, a.pe);
}
if (angle_s == 360) { angle_s = 0; }
if (angle_e >= angle_s)
angle_sum = 360 - Math.Abs(angle_s - angle_e);
else
angle_sum = Math.Abs(angle_s - angle_e);
return angle_sum;
}
View Code
3.Point,PAD,Line,Arc数据结构
/// <summary>
/// 精简 PAD 数据类型
/// </summary>
public struct gPP
{
public gPP(double x_val, double y_val, double width_)
{
this.p = new gPoint(x_val, y_val);
this.symbols = "r";
this.width = width_;
}
public gPP(gPoint p_, double width_)
{
this.p = p_;
this.symbols = "r";
this.width = width_;
}
public gPP(gPoint p_, string symbols_, double width_)
{
this.p = p_;
this.symbols = symbols_;
this.width = width_;
}
public gPoint p;
public string symbols;
public double width;
public static gPP operator +(gPP p1, gPP p2)
{
p1.p += p2.p;
return p1;
}
public static gPP operator +(gPP p1, gPoint p2)
{
p1.p += p2;
return p1;
}
public static gPP operator -(gPP p1, gPP p2)
{
p1.p -= p2.p;
return p1;
}
public static gPP operator -(gPP p1, gPoint p2)
{
p1.p -= p2;
return p1;
}
}
/// <summary>
/// 点 数据类型 (XY)
/// </summary>
public struct gPoint
{
public gPoint(gPoint p_)
{
this.x = p_.x;
this.y = p_.y;
}
public gPoint(double x_val, double y_val)
{
this.x = x_val;
this.y = y_val;
}
public double x;
public double y;
public static gPoint operator +(gPoint p1, gPoint p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
public static gPoint operator -(gPoint p1, gPoint p2)
{
p1.x -= p2.x;
p1.y -= p2.y;
return p1;
}
}
/// <summary>
/// Line 数据类型
/// </summary>
public struct gL
{
public gL(double ps_x, double ps_y, double pe_x, double pe_y, double width_)
{
this.ps = new gPoint(ps_x, ps_y);
this.pe = new gPoint(pe_x, pe_y);
this.negative = false;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gL(gPoint ps_, gPoint pe_, double width_)
{
this.ps = ps_;
this.pe = pe_;
this.negative = false;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gL(gPoint ps_, gPoint pe_, string symbols_, double width_)
{
this.ps = ps_;
this.pe = pe_;
this.negative = false;
this.symbols = symbols_;
this.attribut = string.Empty;
this.width = width_;
}
public gPoint ps;
public gPoint pe;
public bool negative;//polarity-- positive negative
public string symbols;
public string attribut;
public double width;
public static gL operator +(gL l1, gPoint move_p)
{
l1.ps += move_p;
l1.pe += move_p;
return l1;
}
public static gL operator +(gL l1, gP move_p)
{
l1.ps += move_p.p;
l1.pe += move_p.p;
return l1;
}
public static gL operator -(gL l1, gPoint move_p)
{
l1.ps -= move_p;
l1.pe -= move_p;
return l1;
}
public static gL operator -(gL l1, gP move_p)
{
l1.ps -= move_p.p;
l1.pe -= move_p.p;
return l1;
}
}
/// <summary>
/// ARC 数据类型
/// </summary>
public struct gA
{
public gA(double ps_x, double ps_y, double pc_x, double pc_y, double pe_x, double pe_y, double width_, bool ccw_)
{
this.ps = new gPoint(ps_x, ps_y);
this.pc = new gPoint(pc_x, pc_y);
this.pe = new gPoint(pe_x, pe_y);
this.negative = false;
this.ccw = ccw_;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gA(gPoint ps_, gPoint pc_, gPoint pe_, double width_, bool ccw_ = false)
{
this.ps = ps_;
this.pc = pc_;
this.pe = pe_;
this.negative = false;
this.ccw = ccw_;
this.symbols = "r";
this.attribut = string.Empty;
this.width = width_;
}
public gPoint ps;
public gPoint pe;
public gPoint pc;
public bool negative;//polarity-- positive negative
public bool ccw; //direction-- cw ccw
public string symbols;
public string attribut;
public double width;
public static gA operator +(gA arc1, gPoint move_p)
{
arc1.ps += move_p;
arc1.pe += move_p;
arc1.pc += move_p;
return arc1;
}
public static gA operator +(gA arc1, gP move_p)
{
arc1.ps += move_p.p;
arc1.pe += move_p.p;
arc1.pc += move_p.p;
return arc1;
}
public static gA operator -(gA arc1, gPoint move_p)
{
arc1.ps -= move_p;
arc1.pe -= move_p;
arc1.pc -= move_p;
return arc1;
}
public static gA operator -(gA arc1, gP move_p)
{
arc1.ps -= move_p.p;
arc1.pe -= move_p.p;
arc1.pc -= move_p.p;
return arc1;
}
}
View Code