794 lines
26 KiB
TypeScript
794 lines
26 KiB
TypeScript
|
import { CCode, CCodeParams, FaceType, GCode, GCodeParams, INcWriter, IPoint, Knife, NcAction, NcActionType, } from "cut-abstractions";
|
|||
|
// // import { CArc2GCode, NcArcType, NcReductionType } from "mes-processors"
|
|||
|
// import { Vector2 } from "node_modules/mes-processors/src/math/vector2";
|
|||
|
|
|||
|
|
|||
|
/** 用以对接 NC类型的加工文件--即 解析器 */
|
|||
|
export class NcConverter implements INcWriter {
|
|||
|
/** NC加工动作记录 */
|
|||
|
|
|||
|
private lines: string[] = [];
|
|||
|
/** 刀库 */
|
|||
|
knifeList: Array<Knife> = []
|
|||
|
/** 当前刀具 */
|
|||
|
private currentKnife?: Knife = undefined;
|
|||
|
private actionRecord: NcAction[] = [];
|
|||
|
arcType: NcArcType = 'R';
|
|||
|
/** 最后一行代码参数 */
|
|||
|
// private lastParams?: any
|
|||
|
private lastParams: GCodeParams = new GCodeParams();
|
|||
|
private lastCode: string = '';
|
|||
|
/** 可以做代码转换 如G2转G3,G3转G2等 */
|
|||
|
codeMap: Record<string, string> = {};
|
|||
|
get ncActions(): NcAction[] {
|
|||
|
return this.actionRecord;
|
|||
|
}
|
|||
|
reductionType: NcReductionType = NcReductionType.None;
|
|||
|
/** 配置 这里给默认值*/
|
|||
|
config: NcConverterConfig = {
|
|||
|
isEnableConverterAxis: false,
|
|||
|
thickness: 18,
|
|||
|
doSimpleFirstCode: false,
|
|||
|
isNcFileComment: true,
|
|||
|
isNcLinePrefixEnabled: false,
|
|||
|
ncLinePrefix: '',
|
|||
|
isUseSimpleCode: false,
|
|||
|
isSimpleFirstCode: false,
|
|||
|
arcType: 'R',
|
|||
|
reverseArcCode: false,
|
|||
|
NcCodeFreeMove: 'G00',
|
|||
|
NcCodeLineInterpolation: 'G01',
|
|||
|
NcCodeClockwiseArcInterpolation: 'G02',
|
|||
|
NcCodeAnticlockwiseArcInterpolation: 'G03',
|
|||
|
NcCodeAxisX: 'X',
|
|||
|
NcCodeAxisY: 'Y',
|
|||
|
NcCodeAxisZ: 'Z',
|
|||
|
NcCodeSpeed: 'F',
|
|||
|
NcCodeIncrementAxisX: 'I',
|
|||
|
NcCodeIncrementAxisY: 'J',
|
|||
|
NcCodeIncrementAxisZ: 'K',
|
|||
|
leaderChar: '//',
|
|||
|
boardLength: 2440,
|
|||
|
boardWidth: 1220,
|
|||
|
boardHeight: 50,
|
|||
|
originPointPosition: BoardPosition.LEFT_TOP,
|
|||
|
originZ0Position: OriginZPosition.WorkTop,
|
|||
|
heightAxis: AxisType.Z_POS,
|
|||
|
decimalPointPrecision: 3,
|
|||
|
fixFloatNumberEndZero: true,
|
|||
|
intNumberAddDecimalPoint: true,
|
|||
|
lineBreak: '\n'
|
|||
|
}
|
|||
|
/** G代码转换关系 */
|
|||
|
codeTransform = {
|
|||
|
[GCode.G0]: this.config.NcCodeFreeMove,
|
|||
|
[GCode.G1]: this.config.NcCodeLineInterpolation,
|
|||
|
[GCode.G2]: this.config.NcCodeClockwiseArcInterpolation,
|
|||
|
[GCode.G3]: this.config.NcCodeAnticlockwiseArcInterpolation
|
|||
|
}
|
|||
|
initConfig(conf: NcConverterConfig) {
|
|||
|
this.config = { ...this.config, ...conf }
|
|||
|
|
|||
|
/** 更新下代码转换关系 */
|
|||
|
this.codeTransform = {
|
|||
|
[GCode.G0]: this.config.NcCodeFreeMove,
|
|||
|
[GCode.G1]: this.config.NcCodeLineInterpolation,
|
|||
|
[GCode.G2]: this.config.NcCodeClockwiseArcInterpolation,
|
|||
|
[GCode.G3]: this.config.NcCodeAnticlockwiseArcInterpolation
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected code(code: keyof typeof GCode, params: Partial<GCodeParams>) {
|
|||
|
const line: any[] = [];
|
|||
|
|
|||
|
/**
|
|||
|
* G0 - G3 代码 显示的时候可以配置
|
|||
|
* 例
|
|||
|
* G00 - G03
|
|||
|
*/
|
|||
|
|
|||
|
if (this.reductionType & NcReductionType.Code) {
|
|||
|
if (this.lastCode != code) {
|
|||
|
line.push(this.codeTransform[code]);
|
|||
|
}
|
|||
|
} else {
|
|||
|
line.push(this.codeTransform[code]);
|
|||
|
}
|
|||
|
|
|||
|
this.lastCode = code;
|
|||
|
|
|||
|
let x = params.x ??= this.lastParams.x;
|
|||
|
let y = params.y ??= this.lastParams.y;
|
|||
|
let z = params.z ??= this.lastParams.z;
|
|||
|
|
|||
|
let x_val = this.handleValue_DecimalPointPrecision(x)
|
|||
|
let y_val = this.handleValue_DecimalPointPrecision(y)
|
|||
|
let z_val = this.handleValue_DecimalPointPrecision(z)
|
|||
|
|
|||
|
let x_code = this.config.NcCodeAxisX || 'X'
|
|||
|
let y_code = this.config.NcCodeAxisY || 'Y'
|
|||
|
let z_code = this.config.NcCodeAxisZ || 'Z'
|
|||
|
|
|||
|
if (this.reductionType & NcReductionType.Position) {
|
|||
|
if (x != this.lastParams.x) {
|
|||
|
line.push(x_code + x_val);
|
|||
|
}
|
|||
|
if (y != this.lastParams.y) {
|
|||
|
line.push(y_code + y_val);
|
|||
|
}
|
|||
|
if (z != this.lastParams.z) {
|
|||
|
line.push(z_code + z_val);
|
|||
|
}
|
|||
|
} else {
|
|||
|
line.push(x_code + x_val);
|
|||
|
line.push(y_code + y_val);
|
|||
|
line.push(z_code + z_val);
|
|||
|
}
|
|||
|
|
|||
|
if (code == 'G2' || code == 'G3') {
|
|||
|
if (this.config.arcType == 'R') {
|
|||
|
|
|||
|
let r = params.r ??= this.lastParams.r;
|
|||
|
|
|||
|
let r_val = this.handleValue_DecimalPointPrecision(r)
|
|||
|
|
|||
|
line.push('R' + r_val);
|
|||
|
}
|
|||
|
if (this.config.arcType == 'IJK') {
|
|||
|
let i = params.i ??= this.lastParams.i;
|
|||
|
let j = params.j ??= this.lastParams.j;
|
|||
|
|
|||
|
let i_val = this.handleValue_DecimalPointPrecision(i)
|
|||
|
let j_val = this.handleValue_DecimalPointPrecision(j)
|
|||
|
|
|||
|
line.push('I' + i_val);
|
|||
|
line.push('J' + j_val);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
const speed = params.f ??= this.lastParams.f
|
|||
|
if (speed != 0) {
|
|||
|
if (this.reductionType & NcReductionType.Speed) {
|
|||
|
if (speed != this.lastParams.f) {
|
|||
|
line.push('F' + speed);
|
|||
|
}
|
|||
|
} else {
|
|||
|
line.push('F' + speed);
|
|||
|
}
|
|||
|
}
|
|||
|
Object.assign(this.lastParams, params); // 更新上一次参数
|
|||
|
if (this.codeMap[line[0]]) { // 命令转换
|
|||
|
line[0] = this.codeMap[line[0]];
|
|||
|
}
|
|||
|
this.lines.push(line.join(' '));
|
|||
|
}
|
|||
|
|
|||
|
gCode<TCode extends (keyof typeof GCode | keyof typeof CCode)>(code: TCode, params: Partial<TCode extends keyof typeof GCode ? GCodeParams : CCodeParams>): void {
|
|||
|
switch (code) {
|
|||
|
case 'G0':
|
|||
|
case 'G1':
|
|||
|
case 'G2':
|
|||
|
case 'G3': {
|
|||
|
this.code(code, params);
|
|||
|
break;
|
|||
|
}
|
|||
|
// 自定义代码
|
|||
|
case 'CArc': {
|
|||
|
// 凸度转GCode
|
|||
|
const cParam = params as Partial<CCodeParams>;
|
|||
|
if (!cParam.x || !cParam.y || !cParam.b) throw new Error("CArc命令缺少必要参数(X, Y, B)");
|
|||
|
const targetPoint = { x: cParam.x, y: cParam.y };
|
|||
|
if (this.config.arcType === 'R') {
|
|||
|
const result = CArc2GCode(this.lastParams, targetPoint, cParam.b, 'R');
|
|||
|
this.code(result.gCode, { ...targetPoint, r: result.r });
|
|||
|
} else {
|
|||
|
const result = CArc2GCode(this.lastParams, targetPoint, cParam.b, 'IJK');
|
|||
|
this.code(result.gCode, { ...targetPoint, i: result.i, j: result.j });
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/** 处理值 最终显示的值 小数点后X位功能 */
|
|||
|
handleValue_DecimalPointPrecision(val: number) {
|
|||
|
const { fixFloatNumberEndZero, intNumberAddDecimalPoint, decimalPointPrecision } = this.config
|
|||
|
/**
|
|||
|
* 2种方式 末尾补零 或者 直接保留小数点后N位
|
|||
|
*/
|
|||
|
let isToFix = false
|
|||
|
|
|||
|
let resVal
|
|||
|
|
|||
|
if (fixFloatNumberEndZero == true) {
|
|||
|
// 末尾补零
|
|||
|
isToFix = true
|
|||
|
} else if (intNumberAddDecimalPoint == true) {
|
|||
|
// 整数值末尾加小数点
|
|||
|
if (Number.isInteger(val)) {
|
|||
|
isToFix = true
|
|||
|
}
|
|||
|
}
|
|||
|
if (isToFix) {
|
|||
|
resVal = val.toFixed(decimalPointPrecision)
|
|||
|
} else {
|
|||
|
resVal = val.toString()
|
|||
|
}
|
|||
|
|
|||
|
return resVal
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/** 更换刀具 */
|
|||
|
changeKnife() {
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/** 校验值是否有效 不为'' 或 undefined */
|
|||
|
checkVal(val: any): boolean {
|
|||
|
let r = true
|
|||
|
if ((val == undefined || val == '')) {
|
|||
|
r = false
|
|||
|
}
|
|||
|
return r
|
|||
|
}
|
|||
|
toString() {
|
|||
|
return this.lines.join(this.config.lineBreak);
|
|||
|
}
|
|||
|
comment(content: string): string {
|
|||
|
|
|||
|
let markContent = content + this.config.lineBreak
|
|||
|
let isShowMark = this.config.isNcFileComment || false
|
|||
|
if (isShowMark) {
|
|||
|
let leaderChar = this.config.leaderChar || ''
|
|||
|
markContent = `${leaderChar} ${markContent}`
|
|||
|
} else {
|
|||
|
markContent = ''
|
|||
|
}
|
|||
|
|
|||
|
return markContent + this.config.lineBreak
|
|||
|
}
|
|||
|
|
|||
|
recordAction(type: NcActionType): string {
|
|||
|
const id = this.createActionId();
|
|||
|
const act: NcAction = {
|
|||
|
id: id,
|
|||
|
type,
|
|||
|
lineIndex: this.lines.length
|
|||
|
};
|
|||
|
this.comment(`CMP ${act.id} ${act.type}`);
|
|||
|
this.actionRecord.push(act);
|
|||
|
return id;
|
|||
|
}
|
|||
|
private _actionIdx = 0;
|
|||
|
private createActionId() {
|
|||
|
return `A${this._actionIdx++}`;
|
|||
|
}
|
|||
|
append(str: string) {
|
|||
|
// this.lines.push(str);
|
|||
|
}
|
|||
|
|
|||
|
appendLine(str: string) {
|
|||
|
|
|||
|
}
|
|||
|
/**
|
|||
|
*
|
|||
|
* @param point 加工项的点
|
|||
|
* @param processItemInfo 加工项的信息 水平基准、垂直基准、轴方向(x、y、z),板件方向(x、y、z)
|
|||
|
* // 这里加工项的点数据 都是经过数据处理的 假定这里拿到的数据都是基于左上角 台面
|
|||
|
*
|
|||
|
* @returns 实际加工项的点
|
|||
|
*/
|
|||
|
getXYZ(point: CodeParams, processItemInfo: ProcessInfo): CodeParams {
|
|||
|
let newPoint: any = {}
|
|||
|
|
|||
|
if (this.config.isEnableConverterAxis) {
|
|||
|
// 进行坐标轴转换
|
|||
|
for (const key in point) {
|
|||
|
if (point[key] != undefined) {
|
|||
|
Reflect.set(newPoint, key, parseFloat(point[key]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// /** 有2个部分
|
|||
|
// * 一个是基于机台和板件的转换 依据板件定位
|
|||
|
// * 另外一个是基于板件和加工项的转换 依据板件长高方向*/
|
|||
|
// switch (this.config.originZ0Position) {
|
|||
|
// case 0:
|
|||
|
// // 台面 不操作
|
|||
|
// break;
|
|||
|
// case 1:
|
|||
|
// // 板面 Z坐标需要转换
|
|||
|
// newPoint.z = newPoint.z - this.config.thickness
|
|||
|
// break;
|
|||
|
// default:
|
|||
|
// break;
|
|||
|
// }
|
|||
|
|
|||
|
// /** step 先转换板件的位置 */
|
|||
|
// // 大板定位 不同 根据不同的定位点修改
|
|||
|
|
|||
|
// // processItemInfo
|
|||
|
// switch (this.config.originPointPosition) {
|
|||
|
// case BoardPosition.LEFT_TOP:
|
|||
|
// // 不操作
|
|||
|
// newPoint = this.changeXYZAxiosSide(newPoint)
|
|||
|
// break;
|
|||
|
// case BoardPosition.LEFT_BOTTOM:
|
|||
|
// // 左下角 x坐标要转换
|
|||
|
// newPoint.x = newPoint.x + this.config.boardWidth - processItemInfo.block.cutWidth //400
|
|||
|
// newPoint = this.changeXYZAxiosSide(newPoint)
|
|||
|
// break;
|
|||
|
// case BoardPosition.RIGHT_TOP:
|
|||
|
// // 右上角 y坐标要转换
|
|||
|
// newPoint.y = newPoint.y + this.config.boardLength - processItemInfo.block.cutLength // 600
|
|||
|
// newPoint = this.changeXYZAxiosSide(newPoint)
|
|||
|
// break;
|
|||
|
// case BoardPosition.RIGHT_BOTTOM:
|
|||
|
// // 右下角 xy 坐标要转换
|
|||
|
// newPoint.x = newPoint.x + this.config.boardWidth - processItemInfo.block.cutWidth
|
|||
|
// newPoint.y = newPoint.y + this.config.boardLength - processItemInfo.block.cutLength
|
|||
|
// newPoint = this.changeXYZAxiosSide(newPoint)
|
|||
|
// break;
|
|||
|
// default:
|
|||
|
// break;
|
|||
|
// }
|
|||
|
|
|||
|
// 这里做 数值的小数点处理
|
|||
|
for (const key in newPoint) {
|
|||
|
if (['x', 'y', 'z', 'r', 'i', 'j', 'k'].includes(key)) {
|
|||
|
let isTofix = false
|
|||
|
if (this.config.fixFloatNumberEndZero == true) {
|
|||
|
// 末尾补零
|
|||
|
isTofix = true
|
|||
|
} else if (this.config.intNumberAddDecimalPoint == true) {
|
|||
|
// 整数值末尾加小数点
|
|||
|
if (Number.isInteger(newPoint[key])) {
|
|||
|
isTofix = true
|
|||
|
}
|
|||
|
}
|
|||
|
if (isTofix) {
|
|||
|
newPoint[key] = parseFloat(newPoint[key]).toFixed(this.config.decimalPointPrecision)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return newPoint
|
|||
|
} else {
|
|||
|
return point
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
/** 根据 轴向变更坐标 */
|
|||
|
changeXYZAxiosSide(point: CodeParams) {
|
|||
|
let newPoint: any = {}
|
|||
|
for (const key in point) {
|
|||
|
if (point[key] != undefined) {
|
|||
|
Reflect.set(newPoint, key, parseFloat(point[key]))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
let width = this.config.boardWidth
|
|||
|
let length = this.config.boardLength
|
|||
|
let height = this.config.boardHeight
|
|||
|
|
|||
|
|
|||
|
if (this.config.widthSideAxis == AxisType.X_POS && this.config.lengthSideAxis == AxisType.Y_POS) {
|
|||
|
// 默认 为 X = x 正 Y = y 正 不操作
|
|||
|
} else if (this.config.widthSideAxis == AxisType.Y_POS && this.config.lengthSideAxis == AxisType.X_POS) {
|
|||
|
// x = y正 y = x正 X Y坐标 倒转
|
|||
|
newPoint = { ...newPoint, x: newPoint.y, y: newPoint.x }
|
|||
|
}
|
|||
|
|
|||
|
else if (this.config.widthSideAxis == AxisType.X_NEG && this.config.lengthSideAxis == AxisType.Y_POS) {
|
|||
|
// x = x负 y = y正
|
|||
|
newPoint = { ...newPoint, x: newPoint.x - width, y: newPoint.y }
|
|||
|
} else if (this.config.widthSideAxis == AxisType.Y_POS && this.config.lengthSideAxis == AxisType.X_NEG) {
|
|||
|
// x = y正 y = x负
|
|||
|
newPoint = { ...newPoint, x: newPoint.y - width, y: newPoint.x }
|
|||
|
}
|
|||
|
|
|||
|
else if (this.config.widthSideAxis == AxisType.X_NEG && this.config.lengthSideAxis == AxisType.Y_NEG) {
|
|||
|
// x = x负 y = y负
|
|||
|
newPoint = { ...newPoint, x: newPoint.x - width, y: newPoint.y - length }
|
|||
|
} else if (this.config.widthSideAxis == AxisType.Y_NEG && this.config.lengthSideAxis == AxisType.X_NEG) {
|
|||
|
// x = y负 y = x负
|
|||
|
newPoint = { ...newPoint, x: newPoint.y - width, y: newPoint.x - length }
|
|||
|
}
|
|||
|
|
|||
|
else if (this.config.widthSideAxis == AxisType.X_POS && this.config.lengthSideAxis == AxisType.Y_NEG) {
|
|||
|
// x = x正 y = y负
|
|||
|
newPoint = { ...newPoint, x: newPoint.x, y: newPoint.y - length }
|
|||
|
} else if (this.config.widthSideAxis == AxisType.Y_NEG && this.config.lengthSideAxis == AxisType.X_POS) {
|
|||
|
// x = y负 y = x正
|
|||
|
newPoint = { ...newPoint, x: newPoint.y, y: newPoint.x - length }
|
|||
|
}
|
|||
|
|
|||
|
if (this.config.heightAxis == AxisType.Z_NEG) {
|
|||
|
// Z轴负
|
|||
|
newPoint = { ...newPoint, z: newPoint.z - height }
|
|||
|
}
|
|||
|
|
|||
|
return newPoint
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
export type NcConverterConfig = {
|
|||
|
/** 是否启用解析器的坐标系转换 */
|
|||
|
isEnableConverterAxis?: boolean,
|
|||
|
/** 板厚 */
|
|||
|
thickness: number,
|
|||
|
/**是否执行换刀后的第一行精简指令 */
|
|||
|
doSimpleFirstCode?: boolean
|
|||
|
/** 是否添加注释信息 */
|
|||
|
isNcFileComment?: boolean
|
|||
|
/** 是否空行插入前缀 */
|
|||
|
isNcLinePrefixEnabled?: boolean
|
|||
|
/** 空行插入前缀 前缀内容*/
|
|||
|
ncLinePrefix?: string
|
|||
|
/** 使用精简指令 */
|
|||
|
isUseSimpleCode?: boolean
|
|||
|
/** 精简换刀后第一行指令 */
|
|||
|
isSimpleFirstCode?: boolean
|
|||
|
/** 圆弧指令模式类型 */
|
|||
|
arcType?: NcArcType
|
|||
|
/** 反转圆弧指令 */
|
|||
|
reverseArcCode?: boolean
|
|||
|
/** 空程移动指令 */
|
|||
|
NcCodeFreeMove?: string
|
|||
|
/** 直线插补标识 */
|
|||
|
NcCodeLineInterpolation?: string
|
|||
|
/** 顺时针圆弧插补标识 */
|
|||
|
NcCodeClockwiseArcInterpolation?: string
|
|||
|
/** 逆时针圆弧插补标识 */
|
|||
|
NcCodeAnticlockwiseArcInterpolation?: string
|
|||
|
/** 水平坐标横轴标识 */
|
|||
|
NcCodeAxisX?: string
|
|||
|
/** 水平坐标纵轴标识 */
|
|||
|
NcCodeAxisY?: string
|
|||
|
/** 垂直坐标轴标识 */
|
|||
|
NcCodeAxisZ?: string
|
|||
|
/** 速度标识 */
|
|||
|
NcCodeSpeed?: string
|
|||
|
/** 水平坐标横轴增量标识 */
|
|||
|
NcCodeIncrementAxisX?: string
|
|||
|
/** 水平坐标纵轴增量标识 */
|
|||
|
NcCodeIncrementAxisY?: string
|
|||
|
/** 垂直坐标轴增量标识 */
|
|||
|
NcCodeIncrementAxisZ?: string
|
|||
|
/** 注释标识符 */
|
|||
|
leaderChar?: string
|
|||
|
/** 工作区域长 x */
|
|||
|
boardLength: number
|
|||
|
/** 工作区域宽 y */
|
|||
|
boardWidth: number
|
|||
|
/** 工作区域高 z */
|
|||
|
boardHeight: number
|
|||
|
/** 水平基准点 */
|
|||
|
originPointPosition?: BoardPosition
|
|||
|
/** 垂直基准点 */
|
|||
|
originZ0Position?: OriginZPosition
|
|||
|
/** 水平纵轴坐标轴向 */
|
|||
|
widthSideAxis?: AxisType
|
|||
|
/** 水平横轴坐标轴向 */
|
|||
|
lengthSideAxis?: AxisType
|
|||
|
/** 垂直轴坐标轴向 */
|
|||
|
heightAxis?: AxisType
|
|||
|
/** 保留小数点位数 */
|
|||
|
decimalPointPrecision?: number
|
|||
|
/** 末尾补零 */
|
|||
|
fixFloatNumberEndZero?: boolean
|
|||
|
/** 整数值末尾加小数点 */
|
|||
|
intNumberAddDecimalPoint?: boolean
|
|||
|
/** 换行符 个别文件需要用 ;\n 结束*/
|
|||
|
lineBreak?: string
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/** 枚举 大板边角位置 */
|
|||
|
export enum BoardPosition {
|
|||
|
/** 左上角 */
|
|||
|
LEFT_TOP = 3,
|
|||
|
/** 左下角 */
|
|||
|
LEFT_BOTTOM = 0,
|
|||
|
/** 右下角 */
|
|||
|
RIGHT_BOTTOM = 1,
|
|||
|
/** 右上角 */
|
|||
|
RIGHT_TOP = 2,
|
|||
|
}
|
|||
|
/** 枚举 坐标轴类型 */
|
|||
|
export enum OriginZPosition {
|
|||
|
/** 台面向上Z轴正 */
|
|||
|
WorkTop = 0,
|
|||
|
/** 板面向上Z轴正 */
|
|||
|
BoardFace = 1,
|
|||
|
}
|
|||
|
|
|||
|
/** 枚举 坐标轴类型 */
|
|||
|
export enum AxisType {
|
|||
|
/** X轴正 */
|
|||
|
X_POS = 0,
|
|||
|
/** X轴负 */
|
|||
|
X_NEG = 1,
|
|||
|
/** Y轴正 */
|
|||
|
Y_POS = 2,
|
|||
|
/** Y轴负 */
|
|||
|
Y_NEG = 3,
|
|||
|
/** 向上Z轴正 */
|
|||
|
Z_POS = 4,
|
|||
|
/** 向下Z轴负 */
|
|||
|
Z_NEG = 5,
|
|||
|
}
|
|||
|
// 加工项 点数据
|
|||
|
export class CodeParams {
|
|||
|
/** x坐标 */
|
|||
|
x?: Number | String
|
|||
|
/** y坐标 */
|
|||
|
y?: Number | String
|
|||
|
/** z坐标 */
|
|||
|
z?: Number | String
|
|||
|
/** 调用的代码编号 */
|
|||
|
dir?: Number | String
|
|||
|
/** 圆弧半径 */
|
|||
|
r?: Number | String
|
|||
|
/** 速度 */
|
|||
|
f?: Number | String
|
|||
|
/** IJK 模式的i */
|
|||
|
i?: Number | String
|
|||
|
/** IJK 模式的j */
|
|||
|
j?: Number | String
|
|||
|
/** IJK 模式的k */
|
|||
|
k?: Number | String
|
|||
|
|
|||
|
/** 代码标识 */
|
|||
|
codeKey?: String
|
|||
|
/** x坐标 */
|
|||
|
xKey?: String
|
|||
|
/** y坐标 */
|
|||
|
yKey?: String
|
|||
|
/** z坐标 */
|
|||
|
zKey?: String
|
|||
|
/** 圆弧半径 */
|
|||
|
rKey?: String
|
|||
|
/** 速度 */
|
|||
|
fKey?: String
|
|||
|
/** IJK 模式的i */
|
|||
|
iKey?: String
|
|||
|
/** IJK 模式的j */
|
|||
|
jKey?: String
|
|||
|
/** IJK 模式的k */
|
|||
|
kKey?: String
|
|||
|
}
|
|||
|
|
|||
|
/** 加工项对应的信息 */
|
|||
|
export class ProcessInfo {
|
|||
|
/**当前加工项的下标*/
|
|||
|
i?: Number
|
|||
|
/** 加工项 对应刀具的数据 */
|
|||
|
knife?: Knife
|
|||
|
/** 加工项的类型 */
|
|||
|
type?: processItemType
|
|||
|
/** 加工项所在的 文件名 */
|
|||
|
belong?: string
|
|||
|
/** 该加工项基于哪个加工面 传入数据 主要用于 加工项坐标转换 感觉可能没用 */
|
|||
|
belongFace?: FaceType
|
|||
|
/** 板件信息 */
|
|||
|
block?: any
|
|||
|
// /** 垂直基准点 */
|
|||
|
// originZ0Position?: OriginZPosition
|
|||
|
// /** 水平基准点 */
|
|||
|
// originPointPosition?: BoardPosition
|
|||
|
// /** 大板定位 */
|
|||
|
// boardLocation?: BoardPosition
|
|||
|
// /** 加工项的宽方向 x */
|
|||
|
// widthSideAxis?: AxisType
|
|||
|
// /** 加工项的长方向 y */
|
|||
|
// lengthSideAxis ?: AxisType
|
|||
|
// /** 加工项的高方向 */
|
|||
|
// heightSideAxis ?: AxisType
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/** 加工项的类别
|
|||
|
* 用于区分加工项的类别,不同的类别 在文件生成的时候 可能需要对应某些独立的节点
|
|||
|
*/
|
|||
|
export enum processItemType {
|
|||
|
/**排钻 */
|
|||
|
Hole = 'hole',
|
|||
|
/** 铣孔 */
|
|||
|
MillingHole = 'millingHole',
|
|||
|
/** 造型 非槽加工 铣型 */
|
|||
|
Model = 'model',
|
|||
|
/** 造型 槽-- 拉槽 */
|
|||
|
Grooves = 'grooves',
|
|||
|
/** 造型 铣槽 */
|
|||
|
MillingGrooves = 'millingGrooves',
|
|||
|
/** 侧面造型 */
|
|||
|
SideModel = 'SideModel',
|
|||
|
/** 侧面槽 - 拉槽 */
|
|||
|
SideGrooves = 'sideGrooves',
|
|||
|
/** 侧面槽 - 拉槽 */
|
|||
|
SideMillingGrooves = 'SideMillingGrooves',
|
|||
|
/** 侧面孔 排钻 */
|
|||
|
SideHole = 'sideHole',
|
|||
|
/** 侧面孔 铣孔 */
|
|||
|
MillingSideHole = 'MillingSideHole',
|
|||
|
/** 开料 */
|
|||
|
CutBlock = 'cutBlock',
|
|||
|
/** 修边 */
|
|||
|
MillingBlockBoard = 'MillingBlockBoard'
|
|||
|
}
|
|||
|
|
|||
|
export type NcArcType = 'R' | 'IJK';
|
|||
|
|
|||
|
/**
|
|||
|
* 将基于凸度的圆弧转换为基于圆弧半径/圆心坐标表示的圆弧,暂不支持三维圆弧(Z轴或K分量)
|
|||
|
* @param source 圆弧起始点
|
|||
|
* @param target 圆弧终点
|
|||
|
* @param bulge 凸度值
|
|||
|
* @param mode 圆弧模式
|
|||
|
* @returns 圆弧参数(IJ或R)
|
|||
|
*/
|
|||
|
export function CArc2GCode(source: IPoint, target: IPoint, bulge: number, mode: 'R'): { gCode: 'G2' | 'G3'; r: number; };
|
|||
|
export function CArc2GCode(source: IPoint, target: IPoint, bulge: number, mode: 'IJK'): { gCode: 'G2' | 'G3'; i: number; j: number; };
|
|||
|
export function CArc2GCode(source: IPoint, target: IPoint, bulge: number, mode: NcArcType): { gCode: 'G2' | 'G3'; r?: number; i?: number; j?: number; } {
|
|||
|
/*
|
|||
|
* ♿♿♿ 修改必看!!!
|
|||
|
* 凸度为圆弧圆心角的1/4正切值
|
|||
|
* Bulge = tan(θ / 4) θ为圆心角
|
|||
|
* 凸度为正数,则从起点顺时针绘制圆弧到终点
|
|||
|
* 凸度为负数,则逆时针绘制
|
|||
|
* 当凸度为0时,绘制直线
|
|||
|
* 当凸度为1时,圆心角为180度 tan(180 / 4) = 1
|
|||
|
* 凸度转换公式见 https://www.lee-mac.com/bulgeconversion.html
|
|||
|
*
|
|||
|
* NC圆弧规则
|
|||
|
* 圆弧半径(R)模式:
|
|||
|
* 从起点到终点绘制半径为|R|的圆弧,R越大圆弧越平滑,越小圆弧越弯曲
|
|||
|
* 当R为正数时,表示绘制圆的短弧
|
|||
|
* 当R为负数时,表示绘制圆的长弧
|
|||
|
* ⚠️注意 起点到终点的距离不可大于 2 * |R|,否则应当抛出错误
|
|||
|
*
|
|||
|
* 圆心坐标(IJ)模式:
|
|||
|
* 从起点到终点绘制圆弧,圆心坐标为(I, J),I为圆心在X轴上的坐标(带符号),J为圆心在Y轴上的坐标(带符号)
|
|||
|
* ⚠️注意 圆心到圆弧起点和圆心到圆弧终点的距离必须相等(都等于圆弧的半径),否则应当抛出错误
|
|||
|
*/
|
|||
|
|
|||
|
if (bulge === 0) {
|
|||
|
throw new Error("圆弧模式下,凸度值不能为0");
|
|||
|
}
|
|||
|
|
|||
|
const p1 = Vector2.FromPoint(source);
|
|||
|
const p2 = Vector2.FromPoint(target);
|
|||
|
|
|||
|
const gCode = bulge > 0 ? 'G2' : 'G3';
|
|||
|
|
|||
|
const delta = p2.Subtract(p1);
|
|||
|
const dist = delta.Magnitude;
|
|||
|
|
|||
|
if (dist < 1e-6) {
|
|||
|
throw new Error("起点与终点距离过近");
|
|||
|
}
|
|||
|
|
|||
|
// 通过凸度值计算圆弧半径
|
|||
|
// 圆弧半径 r = (弦长 / 2) * (1 + bulge²) / (2 * |bulge|)
|
|||
|
// 详见: https://www.lee-mac.com/bulgeconversion.html#bulgeradius
|
|||
|
const chordLength = delta.Magnitude;
|
|||
|
const radius = (chordLength / 2) * (1 + bulge * bulge) / (2 * Math.abs(bulge));
|
|||
|
|
|||
|
// R模式
|
|||
|
if (mode === 'R') {
|
|||
|
return { gCode: gCode, r: radius };
|
|||
|
}
|
|||
|
|
|||
|
// IJK模式
|
|||
|
// 圆心位于弦的垂直平分线
|
|||
|
// 计算弦的中点
|
|||
|
const midPoint = p1.Add(delta.Multiply(0.5));
|
|||
|
// 计算弦心距d
|
|||
|
// 公式:d = √(r² - a²),r为半径,a为弦长的一半(勾股定理)
|
|||
|
const d = Math.sqrt(radius * radius - (dist / 2) * (dist / 2));
|
|||
|
|
|||
|
// 垂直平分线单位向量
|
|||
|
const perpVec = new Vector2(-delta.y, delta.x).Normalize();
|
|||
|
|
|||
|
// 从弦的中点向垂直平分线向量移动弦心距d来计算出圆心的坐标
|
|||
|
const center = midPoint.Add(perpVec.Multiply(d * -Math.sign(bulge)));
|
|||
|
|
|||
|
const i = center.x - p1.x;
|
|||
|
const j = center.y - p1.y;
|
|||
|
|
|||
|
return { gCode, i, j };
|
|||
|
}
|
|||
|
|
|||
|
export enum NcReductionType {
|
|||
|
None = 0,
|
|||
|
Position = 1 << 0,
|
|||
|
Speed = 1 << 1,
|
|||
|
Code = 1 << 2,
|
|||
|
/**
|
|||
|
* 换刀重置
|
|||
|
*/
|
|||
|
SwitchKnifeReset = 1 << 3
|
|||
|
}
|
|||
|
|
|||
|
export class Vector2 implements IPoint {
|
|||
|
static Zero = new Vector2(0, 0);
|
|||
|
static One = new Vector2(1, 1);
|
|||
|
static Up = new Vector2(0, 1);
|
|||
|
static Down = new Vector2(0, -1);
|
|||
|
static Left = new Vector2(-1, 0);
|
|||
|
static Right = new Vector2(1, 0);
|
|||
|
static FromPoint(pt: IPoint) {
|
|||
|
if (pt instanceof Vector2) return pt as Vector2;
|
|||
|
return new Vector2(pt.x, pt.y);
|
|||
|
}
|
|||
|
|
|||
|
x: number;
|
|||
|
y: number;
|
|||
|
|
|||
|
/** 获取向量的模长 */
|
|||
|
get Magnitude() {
|
|||
|
return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2));
|
|||
|
}
|
|||
|
|
|||
|
/** 获取向量的平方模长,这个属性比 `Magnitude` 属性更快 */
|
|||
|
get SquaredMagnitude() {
|
|||
|
return Math.pow(this.x, 2) + Math.pow(this.y, 2);
|
|||
|
}
|
|||
|
|
|||
|
constructor(x: number, y: number) {
|
|||
|
this.x = x;
|
|||
|
this.y = y;
|
|||
|
}
|
|||
|
|
|||
|
/** 克隆向量 */
|
|||
|
Clone(): Vector2 {
|
|||
|
return new Vector2(this.x, this.y);
|
|||
|
}
|
|||
|
|
|||
|
/** 计算两个向量之间的距离 */
|
|||
|
Distance(other: Vector2): number {
|
|||
|
return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
|
|||
|
}
|
|||
|
|
|||
|
/** 计算两个向量之间的平方距离,这个属性比 `Distance` 属性更快 */
|
|||
|
SquaredDistance(other: Vector2): number {
|
|||
|
return Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2);
|
|||
|
}
|
|||
|
|
|||
|
/** 向量和 */
|
|||
|
Add(other: Vector2): Vector2 {
|
|||
|
return new Vector2(this.x + other.x, this.y + other.y);
|
|||
|
}
|
|||
|
|
|||
|
/** 向量差 */
|
|||
|
Subtract(other: Vector2): Vector2 {
|
|||
|
return new Vector2(this.x - other.x, this.y - other.y);
|
|||
|
}
|
|||
|
|
|||
|
/** 向量点乘 */
|
|||
|
Dot(other: Vector2): number {
|
|||
|
return this.x * other.x + this.y * other.y;
|
|||
|
}
|
|||
|
|
|||
|
/** 向量叉乘 */
|
|||
|
Cross(other: Vector2): number {
|
|||
|
return this.x * other.y - this.y * other.x;
|
|||
|
}
|
|||
|
|
|||
|
/** 向量与标量相乘 */
|
|||
|
Multiply(scalar: number): Vector2 {
|
|||
|
return new Vector2(this.x * scalar, this.y * scalar);
|
|||
|
}
|
|||
|
|
|||
|
/** 向量归一化 */
|
|||
|
Normalize(): Vector2 {
|
|||
|
const magnitude = this.Magnitude;
|
|||
|
if (magnitude === 0) {
|
|||
|
return Vector2.Zero;
|
|||
|
}
|
|||
|
return new Vector2(this.x / magnitude, this.y / magnitude);
|
|||
|
}
|
|||
|
}
|