Compare commits
	
		
			1 Commits
		
	
	
		
			0.3
			...
			604bd0910e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 604bd0910e | 
							
								
								
									
										65
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								README.md
									
									
									
									
									
								
							@@ -85,68 +85,3 @@ console.log(context.output?.result); // "Processed: hello with param: world"
 | 
			
		||||
| 大板名 | 无 | boardName | goodsName | |
 | 
			
		||||
| 余料 | 无 | scrap | remain | |
 | 
			
		||||
| 排单 | 无 | planOrder |planOrder | |
 | 
			
		||||
 | 
			
		||||
## 编译与发布  
 | 
			
		||||
 | 
			
		||||
更新 package.json 版本号  
 | 
			
		||||
 | 
			
		||||
```shell  
 | 
			
		||||
pnpm clean  
 | 
			
		||||
pnpm build  
 | 
			
		||||
pnpm release  
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 开发
 | 
			
		||||
 | 
			
		||||
### 处理器类型
 | 
			
		||||
 | 
			
		||||
处理器的上下文类型应当提交至该项目以方便协作,包括处理器输入,输出以及配置类型。  
 | 
			
		||||
这些类型应当定义在`src/models/processors/<处理器名>.ts`文件中。
 | 
			
		||||
例:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
// src/models/processors/rectLayout.ts
 | 
			
		||||
 | 
			
		||||
// 矩形优化处理器类型
 | 
			
		||||
 | 
			
		||||
export interface RectLayoutProcInput {
 | 
			
		||||
  blocks: Array<RectLayoutBlock>
 | 
			
		||||
}
 | 
			
		||||
export type RectLayoutProcOutput = never;
 | 
			
		||||
export class RectLayoutProcConfig extends ConfigBase {}
 | 
			
		||||
 | 
			
		||||
export interface RectLayoutBlock {}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
定义类型时请遵循以下约定:
 | 
			
		||||
 | 
			
		||||
- 处理器的输入/输出/配置类型(以下称为*相关类型*)请按照特定规则进行命名,其大小写应当遵循[PascalCase](https://pascal-case.com/):
 | 
			
		||||
  - 输入类型: `<处理器名>Input`
 | 
			
		||||
  - 输出类型: `<处理器名>Output`
 | 
			
		||||
  - 配置类型: `<处理器名>Config`
 | 
			
		||||
- 处理器的配置类型必须为一个Javascript类,并继承自抽象类`ConfigBase`,输入和输出类型不限。
 | 
			
		||||
- 处理器相关类型的字段若涉及到附属类型,也一并定义在文件中,其命名不限,但必须与处理器的名字有所关联,不易于其它处理器混淆,例:
 | 
			
		||||
 | 
			
		||||
  ```ts
 | 
			
		||||
  export interface RectLayoutProcInput {
 | 
			
		||||
    blocks: Array<RectLayoutBlock> // 附属类型
 | 
			
		||||
  }
 | 
			
		||||
  // 同样定义在该文件中,
 | 
			
		||||
  export interface RectLayoutBlock {}
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
- 若处理器相关类型中出现了唯一标识符,请统一使用`string | number`作为Typescript类型,例:
 | 
			
		||||
 | 
			
		||||
  ```ts
 | 
			
		||||
  export interface RectLayoutBlock {
 | 
			
		||||
    id: string | number; // 使用string | number类型作为唯一标识符  
 | 
			
		||||
  }
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
- 若处理器相关类型中出现了平面尺寸相关的,请使用“长(`length`)/宽(`width`)”作为其名称,请勿使用“宽(`width`)/高(`height`)”。
 | 
			
		||||
  
 | 
			
		||||
  > [!Warning]
 | 
			
		||||
  > “长”指的是一个矩形板件在**横轴**上的尺寸,而“宽”指的是**纵轴**上的尺寸。
 | 
			
		||||
  > 
 | 
			
		||||
  > 在旧版生产的数据中,“长/宽”与“宽/高”是混用的,在矩形优化后,原有板件的“长(l)”变为了“高(pbg)”,而“宽(w)”变为了“宽(pbk)”,因为矩形优化涉及到了坐标转换,后续还需要对规范进行统一。
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "cut-abstractions",
 | 
			
		||||
  "version": "0.3.0",
 | 
			
		||||
  "version": "0.1.8",
 | 
			
		||||
  "description": "",
 | 
			
		||||
  "files": [
 | 
			
		||||
    "dist/**/*"
 | 
			
		||||
@@ -28,7 +28,15 @@
 | 
			
		||||
    "@types/jest": "^30.0.0",
 | 
			
		||||
    "cadapi": "http://gitea.cf/MES-FE/webcad-api/archive/0.0.60.tar.gz",
 | 
			
		||||
    "jest": "^30.0.2",
 | 
			
		||||
    "jest-worker": "^30.0.2",
 | 
			
		||||
    "js-angusj-clipper": "^1.0.4",
 | 
			
		||||
    "rimraf": "^6.0.1",
 | 
			
		||||
    "ts-jest": "^29.4.0",
 | 
			
		||||
    "typescript": "^5.8.3"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "jest-worker": "^30.0.2",
 | 
			
		||||
    "three": "^0.178.0",
 | 
			
		||||
    "webworker": "^0.8.4"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										144
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										144
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@@ -7,6 +7,16 @@ settings:
 | 
			
		||||
importers:
 | 
			
		||||
 | 
			
		||||
  .:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      jest-worker:
 | 
			
		||||
        specifier: ^30.0.2
 | 
			
		||||
        version: 30.0.2
 | 
			
		||||
      three:
 | 
			
		||||
        specifier: ^0.178.0
 | 
			
		||||
        version: 0.178.0
 | 
			
		||||
      webworker:
 | 
			
		||||
        specifier: ^0.8.4
 | 
			
		||||
        version: 0.8.4
 | 
			
		||||
    devDependencies:
 | 
			
		||||
      '@jest/globals':
 | 
			
		||||
        specifier: ^30.0.2
 | 
			
		||||
@@ -26,9 +36,15 @@ importers:
 | 
			
		||||
      jest:
 | 
			
		||||
        specifier: ^30.0.2
 | 
			
		||||
        version: 30.0.4(@types/node@24.0.12)
 | 
			
		||||
      js-angusj-clipper:
 | 
			
		||||
        specifier: ^1.0.4
 | 
			
		||||
        version: 1.3.1
 | 
			
		||||
      rimraf:
 | 
			
		||||
        specifier: ^6.0.1
 | 
			
		||||
        version: 6.0.1
 | 
			
		||||
      ts-jest:
 | 
			
		||||
        specifier: ^29.4.0
 | 
			
		||||
        version: 29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.4)(@jest/types@30.0.1)(babel-jest@30.0.4(@babel/core@7.28.0))(jest-util@30.0.2)(jest@30.0.4(@types/node@24.0.12))(typescript@5.8.3)
 | 
			
		||||
      typescript:
 | 
			
		||||
        specifier: ^5.8.3
 | 
			
		||||
        version: 5.8.3
 | 
			
		||||
@@ -624,6 +640,9 @@ packages:
 | 
			
		||||
  argparse@1.0.10:
 | 
			
		||||
    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
 | 
			
		||||
 | 
			
		||||
  async@3.2.6:
 | 
			
		||||
    resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
 | 
			
		||||
 | 
			
		||||
  babel-jest@30.0.4:
 | 
			
		||||
    resolution: {integrity: sha512-UjG2j7sAOqsp2Xua1mS/e+ekddkSu3wpf4nZUSvXNHuVWdaOUXQ77+uyjJLDE9i0atm5x4kds8K9yb5lRsRtcA==}
 | 
			
		||||
    engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
 | 
			
		||||
@@ -667,6 +686,10 @@ packages:
 | 
			
		||||
    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
 | 
			
		||||
  bs-logger@0.2.6:
 | 
			
		||||
    resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
 | 
			
		||||
    engines: {node: '>= 6'}
 | 
			
		||||
 | 
			
		||||
  bser@2.1.1:
 | 
			
		||||
    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
 | 
			
		||||
 | 
			
		||||
@@ -763,6 +786,11 @@ packages:
 | 
			
		||||
  eastasianwidth@0.2.0:
 | 
			
		||||
    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
 | 
			
		||||
 | 
			
		||||
  ejs@3.1.10:
 | 
			
		||||
    resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
 | 
			
		||||
    engines: {node: '>=0.10.0'}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
 | 
			
		||||
  electron-to-chromium@1.5.180:
 | 
			
		||||
    resolution: {integrity: sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==}
 | 
			
		||||
 | 
			
		||||
@@ -810,6 +838,9 @@ packages:
 | 
			
		||||
  fb-watchman@2.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
 | 
			
		||||
 | 
			
		||||
  filelist@1.0.4:
 | 
			
		||||
    resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
 | 
			
		||||
 | 
			
		||||
  fill-range@7.1.1:
 | 
			
		||||
    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
@@ -938,6 +969,11 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==}
 | 
			
		||||
    engines: {node: 20 || >=22}
 | 
			
		||||
 | 
			
		||||
  jake@10.9.2:
 | 
			
		||||
    resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
 | 
			
		||||
  jest-changed-files@30.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-Ius/iRST9FKfJI+I+kpiDh8JuUlAISnRszF9ixZDIqJF17FckH5sOzKC8a0wd0+D+8em5ADRHA5V5MnfeDk2WA==}
 | 
			
		||||
    engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
 | 
			
		||||
@@ -1066,6 +1102,9 @@ packages:
 | 
			
		||||
      node-notifier:
 | 
			
		||||
        optional: true
 | 
			
		||||
 | 
			
		||||
  js-angusj-clipper@1.3.1:
 | 
			
		||||
    resolution: {integrity: sha512-/qru4QXxN/gBbQjL4WaFl296YSM8kh5XKpNuNqfZhJ4t4Hw3KeLc5ERj3XHAeLi6pBrqeh6o9PFZUpS3QThEEQ==}
 | 
			
		||||
 | 
			
		||||
  js-tokens@4.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
 | 
			
		||||
 | 
			
		||||
@@ -1100,6 +1139,9 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
 | 
			
		||||
  lodash.memoize@4.1.2:
 | 
			
		||||
    resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
 | 
			
		||||
 | 
			
		||||
  lru-cache@10.4.3:
 | 
			
		||||
    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
 | 
			
		||||
 | 
			
		||||
@@ -1114,6 +1156,9 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
 | 
			
		||||
  make-error@1.3.6:
 | 
			
		||||
    resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
 | 
			
		||||
 | 
			
		||||
  makeerror@1.0.12:
 | 
			
		||||
    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
 | 
			
		||||
 | 
			
		||||
@@ -1135,6 +1180,10 @@ packages:
 | 
			
		||||
  minimatch@3.1.2:
 | 
			
		||||
    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
 | 
			
		||||
 | 
			
		||||
  minimatch@5.1.6:
 | 
			
		||||
    resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
 | 
			
		||||
  minimatch@9.0.5:
 | 
			
		||||
    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
 | 
			
		||||
    engines: {node: '>=16 || 14 >=14.17'}
 | 
			
		||||
@@ -1354,6 +1403,9 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
 | 
			
		||||
  three@0.178.0:
 | 
			
		||||
    resolution: {integrity: sha512-ybFIB0+x8mz0wnZgSGy2MO/WCO6xZhQSZnmfytSPyNpM0sBafGRVhdaj+erYh5U+RhQOAg/eXqw5uVDiM2BjhQ==}
 | 
			
		||||
 | 
			
		||||
  tmpl@1.0.5:
 | 
			
		||||
    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
 | 
			
		||||
 | 
			
		||||
@@ -1361,6 +1413,33 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
 | 
			
		||||
    engines: {node: '>=8.0'}
 | 
			
		||||
 | 
			
		||||
  ts-jest@29.4.0:
 | 
			
		||||
    resolution: {integrity: sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==}
 | 
			
		||||
    engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@babel/core': '>=7.0.0-beta.0 <8'
 | 
			
		||||
      '@jest/transform': ^29.0.0 || ^30.0.0
 | 
			
		||||
      '@jest/types': ^29.0.0 || ^30.0.0
 | 
			
		||||
      babel-jest: ^29.0.0 || ^30.0.0
 | 
			
		||||
      esbuild: '*'
 | 
			
		||||
      jest: ^29.0.0 || ^30.0.0
 | 
			
		||||
      jest-util: ^29.0.0 || ^30.0.0
 | 
			
		||||
      typescript: '>=4.3 <6'
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@babel/core':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@jest/transform':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@jest/types':
 | 
			
		||||
        optional: true
 | 
			
		||||
      babel-jest:
 | 
			
		||||
        optional: true
 | 
			
		||||
      esbuild:
 | 
			
		||||
        optional: true
 | 
			
		||||
      jest-util:
 | 
			
		||||
        optional: true
 | 
			
		||||
 | 
			
		||||
  tslib@2.8.1:
 | 
			
		||||
    resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
 | 
			
		||||
 | 
			
		||||
@@ -1372,6 +1451,10 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
 | 
			
		||||
  type-fest@4.41.0:
 | 
			
		||||
    resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
 | 
			
		||||
    engines: {node: '>=16'}
 | 
			
		||||
 | 
			
		||||
  typescript@5.8.3:
 | 
			
		||||
    resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
 | 
			
		||||
    engines: {node: '>=14.17'}
 | 
			
		||||
@@ -1396,6 +1479,10 @@ packages:
 | 
			
		||||
  walker@1.0.8:
 | 
			
		||||
    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
 | 
			
		||||
 | 
			
		||||
  webworker@0.8.4:
 | 
			
		||||
    resolution: {integrity: sha512-zzsVxtHf+mCn0WuYLarSWfRGmX7JiYKkKvso5FYC7rJ9G8svwGQA5a51Sjq9D2c/rKVU6U/kyBcaI7gUTVlsJg==}
 | 
			
		||||
    engines: {node: '>=0.4.3'}
 | 
			
		||||
 | 
			
		||||
  which@2.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
 | 
			
		||||
    engines: {node: '>= 8'}
 | 
			
		||||
@@ -2104,6 +2191,8 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      sprintf-js: 1.0.3
 | 
			
		||||
 | 
			
		||||
  async@3.2.6: {}
 | 
			
		||||
 | 
			
		||||
  babel-jest@30.0.4(@babel/core@7.28.0):
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/core': 7.28.0
 | 
			
		||||
@@ -2180,6 +2269,10 @@ snapshots:
 | 
			
		||||
      node-releases: 2.0.19
 | 
			
		||||
      update-browserslist-db: 1.1.3(browserslist@4.25.1)
 | 
			
		||||
 | 
			
		||||
  bs-logger@0.2.6:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      fast-json-stable-stringify: 2.1.0
 | 
			
		||||
 | 
			
		||||
  bser@2.1.1:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      node-int64: 0.4.0
 | 
			
		||||
@@ -2245,6 +2338,10 @@ snapshots:
 | 
			
		||||
 | 
			
		||||
  eastasianwidth@0.2.0: {}
 | 
			
		||||
 | 
			
		||||
  ejs@3.1.10:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      jake: 10.9.2
 | 
			
		||||
 | 
			
		||||
  electron-to-chromium@1.5.180: {}
 | 
			
		||||
 | 
			
		||||
  emittery@0.13.1: {}
 | 
			
		||||
@@ -2292,6 +2389,10 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      bser: 2.1.1
 | 
			
		||||
 | 
			
		||||
  filelist@1.0.4:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      minimatch: 5.1.6
 | 
			
		||||
 | 
			
		||||
  fill-range@7.1.1:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      to-regex-range: 5.0.1
 | 
			
		||||
@@ -2421,6 +2522,13 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@isaacs/cliui': 8.0.2
 | 
			
		||||
 | 
			
		||||
  jake@10.9.2:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      async: 3.2.6
 | 
			
		||||
      chalk: 4.1.2
 | 
			
		||||
      filelist: 1.0.4
 | 
			
		||||
      minimatch: 3.1.2
 | 
			
		||||
 | 
			
		||||
  jest-changed-files@30.0.2:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      execa: 5.1.1
 | 
			
		||||
@@ -2732,6 +2840,8 @@ snapshots:
 | 
			
		||||
      - supports-color
 | 
			
		||||
      - ts-node
 | 
			
		||||
 | 
			
		||||
  js-angusj-clipper@1.3.1: {}
 | 
			
		||||
 | 
			
		||||
  js-tokens@4.0.0: {}
 | 
			
		||||
 | 
			
		||||
  js-yaml@3.14.1:
 | 
			
		||||
@@ -2755,6 +2865,8 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      p-locate: 4.1.0
 | 
			
		||||
 | 
			
		||||
  lodash.memoize@4.1.2: {}
 | 
			
		||||
 | 
			
		||||
  lru-cache@10.4.3: {}
 | 
			
		||||
 | 
			
		||||
  lru-cache@11.1.0: {}
 | 
			
		||||
@@ -2767,6 +2879,8 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      semver: 7.7.2
 | 
			
		||||
 | 
			
		||||
  make-error@1.3.6: {}
 | 
			
		||||
 | 
			
		||||
  makeerror@1.0.12:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      tmpl: 1.0.5
 | 
			
		||||
@@ -2788,6 +2902,10 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      brace-expansion: 1.1.12
 | 
			
		||||
 | 
			
		||||
  minimatch@5.1.6:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      brace-expansion: 2.0.2
 | 
			
		||||
 | 
			
		||||
  minimatch@9.0.5:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      brace-expansion: 2.0.2
 | 
			
		||||
@@ -2970,12 +3088,34 @@ snapshots:
 | 
			
		||||
      glob: 7.2.3
 | 
			
		||||
      minimatch: 3.1.2
 | 
			
		||||
 | 
			
		||||
  three@0.178.0: {}
 | 
			
		||||
 | 
			
		||||
  tmpl@1.0.5: {}
 | 
			
		||||
 | 
			
		||||
  to-regex-range@5.0.1:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      is-number: 7.0.0
 | 
			
		||||
 | 
			
		||||
  ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.4)(@jest/types@30.0.1)(babel-jest@30.0.4(@babel/core@7.28.0))(jest-util@30.0.2)(jest@30.0.4(@types/node@24.0.12))(typescript@5.8.3):
 | 
			
		||||
    dependencies:
 | 
			
		||||
      bs-logger: 0.2.6
 | 
			
		||||
      ejs: 3.1.10
 | 
			
		||||
      fast-json-stable-stringify: 2.1.0
 | 
			
		||||
      jest: 30.0.4(@types/node@24.0.12)
 | 
			
		||||
      json5: 2.2.3
 | 
			
		||||
      lodash.memoize: 4.1.2
 | 
			
		||||
      make-error: 1.3.6
 | 
			
		||||
      semver: 7.7.2
 | 
			
		||||
      type-fest: 4.41.0
 | 
			
		||||
      typescript: 5.8.3
 | 
			
		||||
      yargs-parser: 21.1.1
 | 
			
		||||
    optionalDependencies:
 | 
			
		||||
      '@babel/core': 7.28.0
 | 
			
		||||
      '@jest/transform': 30.0.4
 | 
			
		||||
      '@jest/types': 30.0.1
 | 
			
		||||
      babel-jest: 30.0.4(@babel/core@7.28.0)
 | 
			
		||||
      jest-util: 30.0.2
 | 
			
		||||
 | 
			
		||||
  tslib@2.8.1:
 | 
			
		||||
    optional: true
 | 
			
		||||
 | 
			
		||||
@@ -2983,6 +3123,8 @@ snapshots:
 | 
			
		||||
 | 
			
		||||
  type-fest@0.21.3: {}
 | 
			
		||||
 | 
			
		||||
  type-fest@4.41.0: {}
 | 
			
		||||
 | 
			
		||||
  typescript@5.8.3: {}
 | 
			
		||||
 | 
			
		||||
  undici-types@7.8.0: {}
 | 
			
		||||
@@ -3027,6 +3169,8 @@ snapshots:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      makeerror: 1.0.12
 | 
			
		||||
 | 
			
		||||
  webworker@0.8.4: {}
 | 
			
		||||
 | 
			
		||||
  which@2.0.2:
 | 
			
		||||
    dependencies:
 | 
			
		||||
      isexe: 2.0.0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										152
									
								
								samples/businessCapability/blockDetailHelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								samples/businessCapability/blockDetailHelper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
import { FaceType, PlaceBlock, PlaceBlockDetail, PlaceBorderContour, PlaceMaterial, PlaceStyle, SizeExpand } from "../confClass"
 | 
			
		||||
import { BlockDetailHelperBase } from "../handleAbility/blockDetailHelperBase"
 | 
			
		||||
import { PolylineHelper } from "../handleAbility/common/LayoutEngine/PolylineHelper"
 | 
			
		||||
import { KnifeHelper } from "../handleAbility/knifeHelper"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** 小板明细 相关的计算  孔 造型 以及轮廓 */
 | 
			
		||||
export class BlockDetailHelper extends BlockDetailHelperBase {
 | 
			
		||||
    /** 预铣(板件外扩)值 */
 | 
			
		||||
    preMillingSize = 0;
 | 
			
		||||
 | 
			
		||||
    constructor(config?: any) {
 | 
			
		||||
        super()
 | 
			
		||||
        if(config){
 | 
			
		||||
            this.updateConfig(config) 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    updateConfig(config) {
 | 
			
		||||
        if (config) {
 | 
			
		||||
            let keys = Object.keys(config)
 | 
			
		||||
            for (const key of keys) {
 | 
			
		||||
                if (Reflect.has(this, key)) {
 | 
			
		||||
                    this[key] = config[key]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /** 小板初始化:1各种轮廓|走刀路径|,2尺寸扩展后的轮廓与走刀路径 */
 | 
			
		||||
    initBlock(block: PlaceBlock, pm: PlaceMaterial) {
 | 
			
		||||
        if (block.blockDetail == null) {
 | 
			
		||||
            // 异常情况
 | 
			
		||||
            console.error(`【initBlock()】${block.blockNo}没用板件明细,请检查`);
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (block.blockDetail.borderContour) return // 已初始化
 | 
			
		||||
 | 
			
		||||
        // const pm = block.placeMaterial
 | 
			
		||||
 | 
			
		||||
        const bd = block.blockDetail
 | 
			
		||||
 | 
			
		||||
        if (pm == null) {
 | 
			
		||||
            return // 异常
 | 
			
		||||
        }
 | 
			
		||||
        const cutKnifeR = pm.diameter / 2
 | 
			
		||||
        const cutGap = pm.cutKnifeGap
 | 
			
		||||
        // 开料偏移值 ,矩形波倒角
 | 
			
		||||
        bd.offsetKnifeRadius = pm.diameter / 2
 | 
			
		||||
        bd.isOffsetRounding = true // 默认要倒角
 | 
			
		||||
        bd.preMillingSize = pm.preMillingSize
 | 
			
		||||
 | 
			
		||||
        if (bd.points.length == 0) // 矩形板,设置不倒角
 | 
			
		||||
        {
 | 
			
		||||
            bd.isOffsetRounding = false// sysConfig.RegularBlockFilletCurve;
 | 
			
		||||
        }
 | 
			
		||||
        else // 异形板
 | 
			
		||||
        {
 | 
			
		||||
            // 异形板倒角
 | 
			
		||||
            bd.isOffsetRounding = true //  sysConfig.isUnRegularBlockChamfer
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 初始化 小板基础轮廓
 | 
			
		||||
        // 1.原始轮廓,成品轮廓
 | 
			
		||||
 | 
			
		||||
        const border_final = this.createFinalBorder(bd, block)
 | 
			
		||||
        // 2.开料轮廓 <不含预铣>
 | 
			
		||||
        const border_org = this.createOrgBorder(bd)
 | 
			
		||||
 | 
			
		||||
        bd.borderContour = new PlaceBorderContour(PlaceStyle.FRONT, border_final, border_org)
 | 
			
		||||
 | 
			
		||||
        // 3.开料轮廓_带预铣, 外扩尺寸
 | 
			
		||||
        bd.borderContour.borderPreMilling = border_org
 | 
			
		||||
        bd.preMillingExpandSize = new SizeExpand()
 | 
			
		||||
        // console.log('开料轮廓_带预铣, 外扩尺寸', block, bd.preMillingExpandSize, sysConfig.preMillingSize)
 | 
			
		||||
        if (this.preMillingSize > 0.0001) {
 | 
			
		||||
            const rt = this.creatOrgBorderWithPrevCut(block, border_org, this.sysConfig.preMillingSize)
 | 
			
		||||
            bd.borderContour.borderPreMilling = rt.newBorders
 | 
			
		||||
            bd.preMillingExpandSize = rt.newSizeExpand
 | 
			
		||||
            // console.log('开料轮廓_带预铣, 外扩尺寸==》after', block, rt.newSizeExpand)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 4. 板内走刀路径,板内空间
 | 
			
		||||
        const innerGroup = this.analyeInners(bd, cutKnifeR, cutGap)
 | 
			
		||||
 | 
			
		||||
        bd.borderContour.borderModelThrough = innerGroup.borders_inner_org
 | 
			
		||||
        bd.borderContour.borderModelThroughR = innerGroup.borders_inner_r
 | 
			
		||||
        bd.borderContour.cutLinesModelThrough = innerGroup.borders_inner_cut
 | 
			
		||||
        bd.borderContour.borderInnerPlace = innerGroup.borders_inner_place
 | 
			
		||||
        bd.borderContour.blockInnerSpace = innerGroup.spaces
 | 
			
		||||
 | 
			
		||||
        // 5 造型外扩轮廓, 外扩尺寸
 | 
			
		||||
        const { pls_model, sizeout: sizeout_model } = this.createPolyline_model(block)
 | 
			
		||||
        bd.borderContour.polylinesOutModel = pls_model
 | 
			
		||||
        bd.modelExpandSize = sizeout_model
 | 
			
		||||
 | 
			
		||||
        // 6. 2V刀路 外扩轮廓,外扩尺寸
 | 
			
		||||
        const { pls_2v, sizeout_2v } = this.createPolyline_2vModel(block)
 | 
			
		||||
        bd.borderContour.polylines2vModel = pls_2v
 | 
			
		||||
        bd.vKnifeModelExpandSize = sizeout_2v
 | 
			
		||||
 | 
			
		||||
        // 7.同刀辅助 外扩尺寸
 | 
			
		||||
        let isUseSameKnifeToHelpCut = this.sysConfig.helpCutStyle || 0
 | 
			
		||||
        let useSameKnifeToHelpCutGap = this.sysConfig.helpCutGap || 0
 | 
			
		||||
        const isSameKnifeToCut = isUseSameKnifeToHelpCut && useSameKnifeToHelpCutGap > 0
 | 
			
		||||
        if (isSameKnifeToCut == false || bd.isUseSameKnifeToHelpCut == false) {
 | 
			
		||||
            // 未启动同刀辅助, 或 该小板 不需要同刀辅助
 | 
			
		||||
            bd.useSameKnifeToHelpCutGap = 0
 | 
			
		||||
            bd.sameKnfieHelpExpandSize = new SizeExpand()
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            const gap = useSameKnifeToHelpCutGap
 | 
			
		||||
            bd.useSameKnifeToHelpCutGap = gap
 | 
			
		||||
            bd.sameKnfieHelpExpandSize = new SizeExpand({ left: gap, right: gap, under: gap, upper: gap })
 | 
			
		||||
        }
 | 
			
		||||
        // 2V刀路,预洗,同刀辅助开料,出板造型刀
 | 
			
		||||
        this.resetBlock(block, pm)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 二维刀路初始化 */
 | 
			
		||||
    init2VModel(blockDetail: PlaceBlockDetail, isCNC = false, _knifeHelper: KnifeHelper) {
 | 
			
		||||
        for (let model of blockDetail.models) {
 | 
			
		||||
            if (!model.isVKnifeModel)
 | 
			
		||||
                continue
 | 
			
		||||
            let vModels: any = []
 | 
			
		||||
            model.VLines = vModels
 | 
			
		||||
            let isFaceB = model.face == FaceType.BACK
 | 
			
		||||
            if (model.pointList.length < 1)
 | 
			
		||||
                continue
 | 
			
		||||
            let ps = model.pointList.map((t) => { return { x: t.pointX, y: t.pointY, bul: t.curve } })
 | 
			
		||||
            let pl = PolylineHelper.create(ps)
 | 
			
		||||
            if (model.VLines?.length > 0)
 | 
			
		||||
                return // 已经分析了
 | 
			
		||||
            model.VLines = []
 | 
			
		||||
            for (let os of model.offsetList) {
 | 
			
		||||
                let knife1 = isCNC ? null : _knifeHelper.getModelKnifeByName(os.name) //  getModelKnifeByName(os.name)
 | 
			
		||||
                let knifeR = os.radius
 | 
			
		||||
                let knifeId = knife1 ? knife1.knifeId : -1
 | 
			
		||||
                try {
 | 
			
		||||
                    let vps_1 = PolylineHelper.getVModelPoints_offset(pl, os.offset, os.depth, os.angle)
 | 
			
		||||
                    let vLine = { isFaceB, name: os.name, value: os.offset, knife: knife1, knifeId, knifeRadius: knifeR, depth: os.depth, points: vps_1, offset: os }
 | 
			
		||||
                    vModels.push(vLine) // 偏移路径
 | 
			
		||||
                    model.VLines.push(vLine)
 | 
			
		||||
                }
 | 
			
		||||
                catch (err) {
 | 
			
		||||
                    console.log('v型刀走刀路径算法出错。' + err)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            model.VLines = vModels
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}  
 | 
			
		||||
							
								
								
									
										283
									
								
								samples/businessCapability/businessRectOptimizeMachine.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								samples/businessCapability/businessRectOptimizeMachine.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,283 @@
 | 
			
		||||
import { ConfigBase } from "../../src/models/config";
 | 
			
		||||
import { confItem, HoleArrange, Knife, PlaceBlock, PlaceBlockDetail, PlaceMaterial } from "../confClass";
 | 
			
		||||
import { Big_bang, ComposingType, LineType, WorkerItemType, xbang } from "../handleAbility/RectOptimizeWorker/bang";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** 配置列表 */
 | 
			
		||||
export const confList: confItem[] = [
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isCutProcess',
 | 
			
		||||
        label: '开料机(雕刻机)加工',
 | 
			
		||||
        value: true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isCutAndCNCProcess',
 | 
			
		||||
        label: '开料机CNC组合',
 | 
			
		||||
        value: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isCustomized',
 | 
			
		||||
        label: '定制加工',
 | 
			
		||||
        value: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'cutBoardBorder',
 | 
			
		||||
        label: '总修边宽度',
 | 
			
		||||
        value: 3
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'blockKnifeLineSpacing',
 | 
			
		||||
        label: '刀路间距',
 | 
			
		||||
        value: 0
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isDoubleFaceBlockFirst',
 | 
			
		||||
        label: '双面开料优先排版',
 | 
			
		||||
        value: true
 | 
			
		||||
    }, 
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isRectPlace',
 | 
			
		||||
        label: '新优化规则排版',
 | 
			
		||||
        value: false
 | 
			
		||||
    }, 
 | 
			
		||||
    {
 | 
			
		||||
        // yuLiaoBoardDo2FaceBlock
 | 
			
		||||
        key: 'isDoubleFaceBlockInRemain',
 | 
			
		||||
        label: '余料板允许排入双面加工的小板',
 | 
			
		||||
        value: true
 | 
			
		||||
    },
 | 
			
		||||
]
 | 
			
		||||
/**
 | 
			
		||||
 * 新优化
 | 
			
		||||
 * 调用流程
 | 
			
		||||
 * 1、准备工作  加载配置 + 加载刀库 + 设置 回调函数
 | 
			
		||||
 * 2、调用 startPlaceThreed 
 | 
			
		||||
 *    入参:data  (数据源 必须包含 小板数据列表和小板明细数据列表)
 | 
			
		||||
 *         pm 优化的材质 
 | 
			
		||||
 *         pm.diameter = knife?.diameter
 | 
			
		||||
           pm.cutKnifeName = knife.knifeName
 | 
			
		||||
 */
 | 
			
		||||
export class BusinessRectOptimizeMachine extends ConfigBase {
 | 
			
		||||
    /** 多线程数据集 */
 | 
			
		||||
    workerList: WorkerItemType[] = []
 | 
			
		||||
   
 | 
			
		||||
    /** 优化回调 */
 | 
			
		||||
    placeCallBack: Function = () => { }
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super()
 | 
			
		||||
       
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
    /** 设置优化回调 */
 | 
			
		||||
    setPlaceTaskCallBackFun(func: Function) {
 | 
			
		||||
        this.placeCallBack = func
 | 
			
		||||
    }
 | 
			
		||||
    /**开始优化线程 输入的数据没有初始化
 | 
			
		||||
     * @param data 数据源 包含小板列表和小板明细列表 data内需要包含blockList(小板列表) 和 blockDetailList(小板明细列表) 
 | 
			
		||||
     * @param pm 优化的材质 包含该材质的可用开料大板信息 需包含开料刀的信息 pm.diameter pm.cutKnifeName
 | 
			
		||||
     */
 | 
			
		||||
    startPlaceThreed(data, pm: PlaceMaterial) {
 | 
			
		||||
        let { blockList, blockDetailList } = data
 | 
			
		||||
        let bList: any = []
 | 
			
		||||
 | 
			
		||||
        blockList.map(e => {
 | 
			
		||||
            if (e.goodsId == pm.goodsId) {
 | 
			
		||||
                bList[e.blockNo] = e
 | 
			
		||||
                let detail = blockDetailList.find(x => x.blockId == e.blockId)
 | 
			
		||||
                if (!Reflect.has(bList[e.blockNo], 'blockDetail')) {
 | 
			
		||||
                    bList[e.blockNo].blockDetail = new PlaceBlockDetail(detail)
 | 
			
		||||
                }
 | 
			
		||||
                // bList[e.blockNo].isTurnFaceToPlace = !this.getDoFace(bList[e.blockNo], this.processMode())
 | 
			
		||||
                // 是否翻面后续处理
 | 
			
		||||
                bList[e.blockNo].isTurnFaceToPlace = true 
 | 
			
		||||
                pm.blockList.push(e)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        pm.cutBorder = this.cutBoardBorder
 | 
			
		||||
        pm.cutKnifeGap = this.blockKnifeLineSpacing
 | 
			
		||||
        
 | 
			
		||||
        /** 小板 */
 | 
			
		||||
        let bans: xbang[] = []
 | 
			
		||||
 | 
			
		||||
        // 实际开料大板的列表
 | 
			
		||||
        let big_Bang: Big_bang[] = []
 | 
			
		||||
        let big_BangSL: number[] = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let border = this.cutBoardBorder
 | 
			
		||||
        let borderOff = (pm.diameter + pm.cutKnifeGap) / 2
 | 
			
		||||
 | 
			
		||||
        // 余料板 以及实际开料大板
 | 
			
		||||
        for (const cuttingBoard of pm.remainBoardList) {
 | 
			
		||||
            big_Bang.push({ w: cuttingBoard.width - border * 2 + borderOff * 2, l: cuttingBoard.length - border * 2 + borderOff * 2, x: border - borderOff, y: border - borderOff })
 | 
			
		||||
            big_BangSL.push(cuttingBoard.count || 999)
 | 
			
		||||
        }
 | 
			
		||||
        // big_Bang = []
 | 
			
		||||
        // big_BangSL = []
 | 
			
		||||
        // 母板 兜底的
 | 
			
		||||
        big_Bang.push({ w: pm.width - border * 2 + borderOff * 2, l: pm.length - border * 2 + borderOff * 2, x: border - borderOff, y: border - borderOff })
 | 
			
		||||
        // 生成小板
 | 
			
		||||
        for (let key in bList) {
 | 
			
		||||
            let b = bList[key]
 | 
			
		||||
 | 
			
		||||
            let bid = b.blockNo
 | 
			
		||||
 | 
			
		||||
            let width = b.placeFullWidth
 | 
			
		||||
            let length = b.placeFullLength
 | 
			
		||||
            let line = this.toLine(b.texture)
 | 
			
		||||
 | 
			
		||||
            bans.push({
 | 
			
		||||
                l: length,
 | 
			
		||||
                w: width,
 | 
			
		||||
                line,
 | 
			
		||||
                face: this.toface(b),
 | 
			
		||||
                id: bid,
 | 
			
		||||
                bno: b.blockNo,
 | 
			
		||||
                holeFaceCount: 3,
 | 
			
		||||
                isRect: !b.isUnRegular,
 | 
			
		||||
                hasHole: false,
 | 
			
		||||
                isdtwosided: true,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let bestCount = 0
 | 
			
		||||
        if (bans.length == 0) // 没有板了
 | 
			
		||||
        {
 | 
			
		||||
            let best = []
 | 
			
		||||
            let yl: Big_bang[] = []
 | 
			
		||||
            let fit = 0
 | 
			
		||||
            let resObj = {
 | 
			
		||||
                data: { bList, best, yl, fit, bans, width: pm.width, length: pm.length, bestCount: bestCount++, pm },
 | 
			
		||||
                info: {
 | 
			
		||||
                    times: -1,
 | 
			
		||||
                    type: 'noBan'
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.placeCallBack(resObj)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        let xyhcs = 50
 | 
			
		||||
        if (bans.length > 1000) { xyhcs = 40 }
 | 
			
		||||
        else if (bans.length > 1500) { xyhcs = 30 }
 | 
			
		||||
        else if (bans.length > 2000) { xyhcs = 25 }
 | 
			
		||||
        else if (bans.length > 4000) { xyhcs = 20 }
 | 
			
		||||
        else if (bans.length > 6000) { xyhcs = 15 }
 | 
			
		||||
        else if (bans.length > 10000) { xyhcs = 10 }
 | 
			
		||||
        else if (bans.length > 15000) { xyhcs = 5 }
 | 
			
		||||
        else if (bans.length > 20000) { xyhcs = 1 }
 | 
			
		||||
        let isDoubleFaceBlockFirst = this.isDoubleFaceBlockFirst // 双面加工排前面
 | 
			
		||||
        let gap = this.blockKnifeLineSpacing
 | 
			
		||||
        // this.bestfit = 0
 | 
			
		||||
 | 
			
		||||
        for (let j = 0; j < 1; j++) {
 | 
			
		||||
 | 
			
		||||
            let w = new Worker(new URL('../handleAbility/RectOptimizeWorker/RectOptimizeWorker.worker', import.meta.url), { type: 'module' })
 | 
			
		||||
            const data = {
 | 
			
		||||
                type: 'start',
 | 
			
		||||
                data: [bans, big_Bang, big_BangSL, xyhcs, isDoubleFaceBlockFirst, gap, this.isRectPlace, this.isDoubleFaceBlockInRemain],
 | 
			
		||||
            }
 | 
			
		||||
            let item: WorkerItemType = {
 | 
			
		||||
                w: w,
 | 
			
		||||
                goodsId: pm.goodsId,
 | 
			
		||||
                pm,
 | 
			
		||||
                status: 'start'
 | 
			
		||||
            }
 | 
			
		||||
            if (this.workerList.findIndex(e => e.goodsId == item.goodsId) == -1) {
 | 
			
		||||
                this.workerList.push(item)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            let workItem = this.workerList.find(e => e.goodsId == pm.goodsId)
 | 
			
		||||
            if (workItem && workItem != undefined) {
 | 
			
		||||
                workItem.w?.postMessage(data)
 | 
			
		||||
 | 
			
		||||
                workItem.w.onmessage = async (d) => {
 | 
			
		||||
                    let [best, yl, fit, info] = d.data as [any[], Big_bang[], number, any]
 | 
			
		||||
 | 
			
		||||
                    switch (info.type) {
 | 
			
		||||
                        case 'loop':
 | 
			
		||||
                            let resObj = {
 | 
			
		||||
                                data: { bList, best, yl, fit, bans, width: pm.width, length: pm.length, bestCount: bestCount++, pm },
 | 
			
		||||
                                info
 | 
			
		||||
                            }
 | 
			
		||||
                            this.placeCallBack(resObj)
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 'stop':
 | 
			
		||||
                            console.error('stop =》dataHandleBase', info, this.workerList)
 | 
			
		||||
                            this.terminateWorker({ goodsId: pm.goodsId })
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 'isStop':
 | 
			
		||||
                            // console.error('isStop', info)
 | 
			
		||||
                            break;
 | 
			
		||||
                        default:
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            console.log('新算法线程启动', pm.goodsId, this.workerList)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /** 停止优化 */
 | 
			
		||||
    async stopPlaceTask() {
 | 
			
		||||
 | 
			
		||||
        for (const key in this.workerList) {
 | 
			
		||||
            await this.terminateWorker({ key })
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取加工面
 | 
			
		||||
     * @param block 
 | 
			
		||||
     * @param processMode 
 | 
			
		||||
     * @returns 
 | 
			
		||||
     */
 | 
			
		||||
    getDoFace(block: PlaceBlock, processMode: number = 0): boolean {
 | 
			
		||||
        // 模式0: 开料机处理排钻, 造型, 采用大孔面作为正面的模式. 正面多加工, cnc多加工
 | 
			
		||||
        if (processMode == 0) {
 | 
			
		||||
            // 先考虑 设计开料面
 | 
			
		||||
            if (block.placeHole == HoleArrange.FRONT)
 | 
			
		||||
                return true
 | 
			
		||||
            if (block.placeHole == HoleArrange.BACK)
 | 
			
		||||
                return false
 | 
			
		||||
            // 造型单面 作为开料面
 | 
			
		||||
            if (block.modelCountFront() > 0 && block.modelCountBack() == 0)
 | 
			
		||||
                return true
 | 
			
		||||
            if (block.modelCountFront() == 0 && block.modelCountBack() > 0)
 | 
			
		||||
                return false
 | 
			
		||||
 | 
			
		||||
            // 优先考虑 大孔面 多孔面
 | 
			
		||||
            return this.getHoleFaceMore(block)
 | 
			
		||||
        } else {
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    processMode() {
 | 
			
		||||
        let processMode = 0;
 | 
			
		||||
        if (this.isCutProcess) processMode = 0;
 | 
			
		||||
        if (this.isCutAndCNCProcess) processMode = 1;
 | 
			
		||||
        if (this.isCustomized) processMode = 2;
 | 
			
		||||
        return processMode;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
   * 正纹 = 0, 可翻转 = 1, 反纹 = 2;
 | 
			
		||||
   * 正文 Positive = 0,  反纹 Reverse = 1,  可翻转 CanReversal = 2,
 | 
			
		||||
   */
 | 
			
		||||
    toLine(texture): LineType {
 | 
			
		||||
        if (texture == 0)
 | 
			
		||||
            return LineType.Positive
 | 
			
		||||
        if (texture == 1)
 | 
			
		||||
            return LineType.CanReversal
 | 
			
		||||
        return LineType.Reverse
 | 
			
		||||
    }
 | 
			
		||||
    /** 小板加工面:Positive=0正面  Reverse=1反面  Arbitrary=2任意 */
 | 
			
		||||
    toface(block: PlaceBlock): ComposingType {
 | 
			
		||||
        let turnF = block.isTurnFaceToPlace
 | 
			
		||||
        if (this.isTurnFaceToPlace)
 | 
			
		||||
            turnF = !turnF
 | 
			
		||||
        if (!turnF)
 | 
			
		||||
            return ComposingType.Positive
 | 
			
		||||
        return ComposingType.Reverse
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								samples/businessCapability/业务功能目录.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								samples/businessCapability/业务功能目录.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2762
									
								
								samples/confClass.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2762
									
								
								samples/confClass.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										33
									
								
								samples/demoDataHandleServer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								samples/demoDataHandleServer.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
import { Processor, ProcessorCollection, ProcessorModule } from "../src/device";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export class ProcessorManager<T, R> implements ProcessorCollection<T, R> {
 | 
			
		||||
    private processors = new Map<string, Processor<T, R>>();
 | 
			
		||||
    private currentProcessor?: Processor<T, R>;
 | 
			
		||||
 | 
			
		||||
    constructor(){
 | 
			
		||||
         
 | 
			
		||||
    }
 | 
			
		||||
    /** 注册模块流程 */
 | 
			
		||||
    registerProcessor(name: string, processor: Processor<T, R>): this {
 | 
			
		||||
        this.processors.set(name, processor);
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
    /** 使用处理器 */
 | 
			
		||||
    useProcessor(name: string): Processor<T, R> {
 | 
			
		||||
        const processor = this.processors.get(name);
 | 
			
		||||
        if (!processor) {
 | 
			
		||||
            throw new Error(`Processor ${name} not found`);
 | 
			
		||||
        }
 | 
			
		||||
        this.currentProcessor = processor;
 | 
			
		||||
        return processor;
 | 
			
		||||
    }
 | 
			
		||||
    /** 获取处理器 */
 | 
			
		||||
    getProcessor(name: string): Processor<T, R> | undefined {
 | 
			
		||||
        return this.processors.get(name);
 | 
			
		||||
    }
 | 
			
		||||
    /** 获取正在使用的处理器 */
 | 
			
		||||
    getCurrentProcessor(): Processor<T, R> | undefined {
 | 
			
		||||
        return this.currentProcessor;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								samples/demoDatahandle/demoDataHandle1.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								samples/demoDatahandle/demoDataHandle1.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
 | 
			
		||||
import { ProcessorModule, StepControllerProcessor } from "../../src/device";
 | 
			
		||||
import { RectOptimizeMachineModule } from "../moduleManager/module1";
 | 
			
		||||
/** 
 | 
			
		||||
 *  demo 开料机处理器
 | 
			
		||||
 * 
 | 
			
		||||
 * 
 | 
			
		||||
*/
 | 
			
		||||
export class demoHandleGroupCutting {
 | 
			
		||||
    processorName = "cutting"
 | 
			
		||||
    processor: StepControllerProcessor<any, any>
 | 
			
		||||
    constructor() {
 | 
			
		||||
 | 
			
		||||
        const callbackStyleModule: ProcessorModule<any, any> = {
 | 
			
		||||
            moduleName: "callbackStyle",
 | 
			
		||||
            process(input, next, context) {
 | 
			
		||||
 | 
			
		||||
                console.log("做优化");
 | 
			
		||||
                const _input = input
 | 
			
		||||
                const _next = next 
 | 
			
		||||
                const _context = context
 | 
			
		||||
                // 可以在这里调用异步操作
 | 
			
		||||
            
 | 
			
		||||
                Reflect.set(context, 'CallBack', callBack1)
 | 
			
		||||
                // 决定是否调用 next
 | 
			
		||||
           
 | 
			
		||||
                function callBack1(v) {
 | 
			
		||||
                    console.log('接收到其它模块回传的数据', v);
 | 
			
		||||
                }
 | 
			
		||||
                // 调用 next 继续流程
 | 
			
		||||
                return next(input);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        const demoModule: ProcessorModule<string, string> = {
 | 
			
		||||
            moduleName: "demoModule",
 | 
			
		||||
 | 
			
		||||
            process(input, next, context) {
 | 
			
		||||
                // 写入上下文
 | 
			
		||||
                context.processedAt = new Date().toLocaleString();
 | 
			
		||||
                context.originalLength = input.length;
 | 
			
		||||
                // 设置下一步需要的上下文
 | 
			
		||||
                context.previousStep = "demoModule";
 | 
			
		||||
                if (context.CallBack) {
 | 
			
		||||
                    context.CallBack("demoModule end and callback")
 | 
			
		||||
                }
 | 
			
		||||
                return next(input);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.processor = new StepControllerProcessor<any, any>();
 | 
			
		||||
        this.processor.use([
 | 
			
		||||
            {
 | 
			
		||||
                moduleName: "traditional",
 | 
			
		||||
                handle(input, next) {
 | 
			
		||||
                    // 第一个流程
 | 
			
		||||
                    console.log(`第一个模块功能:有${input?.blockList.length}片小板,可以做些计算`)
 | 
			
		||||
                    return next ? next(input) : input;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            callbackStyleModule,
 | 
			
		||||
            demoModule,
 | 
			
		||||
            RectOptimizeMachineModule,
 | 
			
		||||
            {
 | 
			
		||||
                moduleName: "final",
 | 
			
		||||
                process(input, next) {
 | 
			
		||||
                    // 不调用 next,终止流程
 | 
			
		||||
                    console.log('结束了')
 | 
			
		||||
                    return next(input);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ])
 | 
			
		||||
 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								samples/demoDatahandle/demoDataHandle2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								samples/demoDatahandle/demoDataHandle2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
import { DeviceBase } from "../../src/device";
 | 
			
		||||
import { ConfigBase } from "../../src/models/config";
 | 
			
		||||
import { BusinessRectOptimizeMachine } from "../businessCapability/businessRectOptimizeMachine";
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
 * 某个处理器  一个 功能 和 业务功能的集合 
 | 
			
		||||
 * 可以包含多个业务功能和 基础功能
 | 
			
		||||
 * 
 | 
			
		||||
 * 
 | 
			
		||||
*/
 | 
			
		||||
export class demoHandleGroup extends DeviceBase {
 | 
			
		||||
    
 | 
			
		||||
    constructor(){
 | 
			
		||||
        super()     
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										112
									
								
								samples/demoKnives.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								samples/demoKnives.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
import { _knifeType, Knife } from "./confClass"
 | 
			
		||||
 | 
			
		||||
/** demo  刀具数据 */
 | 
			
		||||
export const knifeData :Knife= {
 | 
			
		||||
    "isEnabled": true,
 | 
			
		||||
    "axleId": 2,
 | 
			
		||||
    "knifeId": 2,
 | 
			
		||||
    "knifeName": "T1",
 | 
			
		||||
    "knifeType": 3,
 | 
			
		||||
    "ability": [
 | 
			
		||||
        5
 | 
			
		||||
    ],
 | 
			
		||||
    "diameter": 5,
 | 
			
		||||
    "length": 20,
 | 
			
		||||
    "sawThiness": 7,
 | 
			
		||||
    "sawDirection": 2,
 | 
			
		||||
    "processDirection": 4,
 | 
			
		||||
    "speed": 0,
 | 
			
		||||
    "stepDepth": 0,
 | 
			
		||||
    "offsetX": 0,
 | 
			
		||||
    "offsetY": 0,
 | 
			
		||||
    "offsetZ": 0,
 | 
			
		||||
    "baseX": 0,
 | 
			
		||||
    "baseY": 0,
 | 
			
		||||
    "isModularDrill": false,
 | 
			
		||||
    "isPreStartEnabled": false,
 | 
			
		||||
    "preStartAheadActionCount": 5,
 | 
			
		||||
    "isPreStartToolChangeDelay": false,
 | 
			
		||||
    "preStartToolChangeDelayCode": "",
 | 
			
		||||
    "isAxisStartCodePostpost": false,
 | 
			
		||||
    "isAxisStopCodePrepose": false,
 | 
			
		||||
    "drillGroupCode": "",
 | 
			
		||||
    "axisStartCode": "M03 S18000\n",
 | 
			
		||||
    "knifeStartCode": `M06 T1\nG43 H1\n`,
 | 
			
		||||
    "drillGroupStartCode": "T1",
 | 
			
		||||
    "drillGroupEndCode": "",
 | 
			
		||||
    "knifeStopCode": "",
 | 
			
		||||
    "axisStopCode": "M05\n",
 | 
			
		||||
}
 | 
			
		||||
export const knifeData1:_knifeType = {
 | 
			
		||||
    "isEnabled": true,
 | 
			
		||||
    "axleId": 2,
 | 
			
		||||
    "knifeId": 2,
 | 
			
		||||
    "knifeName": "T2",
 | 
			
		||||
    "knifeType": 3,
 | 
			
		||||
    "ability": [
 | 
			
		||||
        5
 | 
			
		||||
    ],
 | 
			
		||||
    "diameter": 6,
 | 
			
		||||
    "length": 20,
 | 
			
		||||
    "sawThiness": 7,
 | 
			
		||||
    "sawDirection": 2,
 | 
			
		||||
    "processDirection": 4,
 | 
			
		||||
    "speed": 0,
 | 
			
		||||
    "stepDepth": 0,
 | 
			
		||||
    "offsetX": 0,
 | 
			
		||||
    "offsetY": 0,
 | 
			
		||||
    "offsetZ": 0,
 | 
			
		||||
    "baseX": 0,
 | 
			
		||||
    "baseY": 0,
 | 
			
		||||
    "isModularDrill": false,
 | 
			
		||||
    "isPreStartEnabled": false,
 | 
			
		||||
    "preStartAheadActionCount": 5,
 | 
			
		||||
    "isPreStartToolChangeDelay": false,
 | 
			
		||||
    "preStartToolChangeDelayCode": "",
 | 
			
		||||
    "isAxisStartCodePostpost": false,
 | 
			
		||||
    "isAxisStopCodePrepose": false,
 | 
			
		||||
    "drillGroupCode": "",
 | 
			
		||||
    "axisStartCode": "M03 S18000\n",
 | 
			
		||||
    "knifeStartCode": `M06 T2\nG43 H2\n`,
 | 
			
		||||
    "drillGroupStartCode": "T2",
 | 
			
		||||
    "drillGroupEndCode": "",
 | 
			
		||||
    "knifeStopCode": "",
 | 
			
		||||
    "axisStopCode": "M05\n",
 | 
			
		||||
}
 | 
			
		||||
export const knifeData2:_knifeType = {
 | 
			
		||||
    "isEnabled": true,
 | 
			
		||||
    "axleId": 2,
 | 
			
		||||
    "knifeId": 2,
 | 
			
		||||
    "knifeName": "T3",
 | 
			
		||||
    "knifeType": 3,
 | 
			
		||||
    "ability": [
 | 
			
		||||
        5
 | 
			
		||||
    ],
 | 
			
		||||
    "diameter": 6,
 | 
			
		||||
    "length": 20,
 | 
			
		||||
    "sawThiness": 7,
 | 
			
		||||
    "sawDirection": 2,
 | 
			
		||||
    "processDirection": 4,
 | 
			
		||||
    "speed": 0,
 | 
			
		||||
    "stepDepth": 0,
 | 
			
		||||
    "offsetX": 0,
 | 
			
		||||
    "offsetY": 0,
 | 
			
		||||
    "offsetZ": 0,
 | 
			
		||||
    "baseX": 0,
 | 
			
		||||
    "baseY": 0,
 | 
			
		||||
    "isModularDrill": false,
 | 
			
		||||
    "isPreStartEnabled": false,
 | 
			
		||||
    "preStartAheadActionCount": 5,
 | 
			
		||||
    "isPreStartToolChangeDelay": false,
 | 
			
		||||
    "preStartToolChangeDelayCode": "",
 | 
			
		||||
    "isAxisStartCodePostpost": false,
 | 
			
		||||
    "isAxisStopCodePrepose": false,
 | 
			
		||||
    "drillGroupCode": "",
 | 
			
		||||
 | 
			
		||||
    "axisStartCode": "M03 S18000\n",
 | 
			
		||||
    "knifeStartCode": `M06 T2\nG43 H2\n`,
 | 
			
		||||
    "drillGroupStartCode": "T3",
 | 
			
		||||
    "drillGroupEndCode": "",
 | 
			
		||||
    "knifeStopCode": "",
 | 
			
		||||
    "axisStopCode": "M05\n",
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										126
									
								
								samples/demoParser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								samples/demoParser.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
 | 
			
		||||
import { ConfigBase } from "../src/base";
 | 
			
		||||
import { ParserBase } from "../src/parsers";
 | 
			
		||||
import { _knifeType, CodeParams } from "./confClass";
 | 
			
		||||
import { knifeData, knifeData1, knifeData2 } from "./demoKnives";
 | 
			
		||||
import { GCodeAction } from "./gcodes";
 | 
			
		||||
export class DemoParser extends ParserBase {
 | 
			
		||||
    usedKnife?:_knifeType
 | 
			
		||||
    knifeList:_knifeType[] = [
 | 
			
		||||
        knifeData,
 | 
			
		||||
        knifeData1,
 | 
			
		||||
        knifeData2
 | 
			
		||||
    ]
 | 
			
		||||
    constructor(config?: Record<string, ConfigBase>) {
 | 
			
		||||
        super();
 | 
			
		||||
        // if(config){
 | 
			
		||||
        //     if(config['knives']){
 | 
			
		||||
        //         
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        const gcodeActions = new GCodeAction();
 | 
			
		||||
        this.codeManager['FSTART'] = {
 | 
			
		||||
            name: 'FileStart',
 | 
			
		||||
            // type:'Global',
 | 
			
		||||
            // paramType:'array',
 | 
			
		||||
            exec:(params) =>{
 | 
			
		||||
                return `G54 G90\nM03\n`
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        this.codeManager['FEND'] = {
 | 
			
		||||
            name: 'FileEnd',
 | 
			
		||||
            exec:(params) =>{
 | 
			
		||||
                return `M30\n`;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.codeManager['G0'] = {
 | 
			
		||||
            name: 'G0',
 | 
			
		||||
            paramType: 'kv:number',
 | 
			
		||||
            exec:(params: CodeParams) => {
 | 
			
		||||
                return gcodeActions.G0(params) + '\n';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.codeManager['G2'] = {
 | 
			
		||||
            name: 'G2',
 | 
			
		||||
            paramType: 'kv:number',
 | 
			
		||||
            exec:(params: CodeParams)=> {
 | 
			
		||||
                return gcodeActions.G2(params) + '\n';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.codeManager['G3'] = {
 | 
			
		||||
            name: 'G3',
 | 
			
		||||
            paramType: 'kv:number',
 | 
			
		||||
            exec:(params: CodeParams)=> {
 | 
			
		||||
                return gcodeActions.G3(params) + '\n';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.codeManager['TD'] = {
 | 
			
		||||
            name: '',
 | 
			
		||||
            exec:(diameter:number)=> {
 | 
			
		||||
                    let code = ''
 | 
			
		||||
                    let _knife: any
 | 
			
		||||
 | 
			
		||||
                    if (diameter) {
 | 
			
		||||
                        _knife = this.knifeList?.find(e => e.diameter == diameter)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    if (_knife != undefined) {
 | 
			
		||||
                        code += this.checkChangeKnife(_knife)
 | 
			
		||||
                        this.usedKnife = _knife
 | 
			
		||||
                        // code += _knife.axisStartCode
 | 
			
		||||
                        code += _knife.knifeStartCode
 | 
			
		||||
                    }
 | 
			
		||||
                    return code
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        this.codeManager['TN'] = {
 | 
			
		||||
            name: 'TN',
 | 
			
		||||
            exec:(knifeName:string) =>{ /**根据刀名称找刀 换刀 */
 | 
			
		||||
                let code = ''
 | 
			
		||||
                let _knife: any = null
 | 
			
		||||
                if (knifeName) {
 | 
			
		||||
                    _knife = this.knifeList?.find(e => e.knifeName == knifeName)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (_knife != undefined) {
 | 
			
		||||
                    code += this.checkChangeKnife(_knife)
 | 
			
		||||
                    this.usedKnife = _knife
 | 
			
		||||
                    // code += _knife.axisStartCode
 | 
			
		||||
                    code += _knife.knifeStartCode
 | 
			
		||||
                }
 | 
			
		||||
                return code
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.codeManager['TE'] = {
 | 
			
		||||
            name:'TE',
 | 
			
		||||
            exec:()=>{
 | 
			
		||||
                if(!this.usedKnife) return '';
 | 
			
		||||
                return this.usedKnife.knifeStopCode+'\n';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 内部功能逻辑  换刀时判断是否需要 上一把刀的停止代码 */
 | 
			
		||||
    private checkChangeKnife(knife: _knifeType) {
 | 
			
		||||
        let code = '';
 | 
			
		||||
        /** true 需要 输出停刀代码  false 不需要 */
 | 
			
		||||
        let flag = false
 | 
			
		||||
        // 必须要有刀
 | 
			
		||||
        if (this.usedKnife) {
 | 
			
		||||
            let keys = Object.keys(knife)
 | 
			
		||||
            for (const key of keys) {
 | 
			
		||||
                if (knife[key] != this.usedKnife[key]) {
 | 
			
		||||
                    flag = true
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (flag) {
 | 
			
		||||
            code += this.exec('TE',[]);
 | 
			
		||||
        }
 | 
			
		||||
        return code;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										528
									
								
								samples/gcodes.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										528
									
								
								samples/gcodes.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,528 @@
 | 
			
		||||
import { CodeParams } from "./confClass"
 | 
			
		||||
 | 
			
		||||
function checkVal(val) {
 | 
			
		||||
    let r = true
 | 
			
		||||
    if ((val == undefined || val == '')) {
 | 
			
		||||
        r = false
 | 
			
		||||
    }
 | 
			
		||||
    return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**G代码指令
 | 
			
		||||
 * 目前 只需要做 G0 - G3
 | 
			
		||||
 */
 | 
			
		||||
export enum GCode {
 | 
			
		||||
    /**快速定位 空程移动 范例:G00 X100 Y50(刀具快速移动至X100、Y50位置) */
 | 
			
		||||
    G0 = 'G0',
 | 
			
		||||
    /**快速定位 空程移动 范例:G00 X100 Y50(刀具快速移动至X100、Y50位置)*/
 | 
			
		||||
    G00 = 'G00',
 | 
			
		||||
    /**直线 直线插补 范例:G01 X200 Y300 F150(刀具以150mm/min的进给速度直线移动至X200、Y300)*/
 | 
			
		||||
    G1 = 'G1',
 | 
			
		||||
    /**直线 范例:G01 X200 Y300 F150(刀具以150mm/min的进给速度直线移动至X200、Y300)*/
 | 
			
		||||
    G01 = 'G01',
 | 
			
		||||
    /** 顺时针 范例:G17 G02 X150 Y50 I50 J0 F100(在XY平面,以半径50mm顺时针圆弧插补至X150、Y50)*/
 | 
			
		||||
    G2 = 'G2',
 | 
			
		||||
    /** 顺时针 范例:G17 G02 X150 Y50 I50 J0 F100(在XY平面,以半径50mm顺时针圆弧插补至X150、Y50)*/
 | 
			
		||||
    G02 = 'G02',
 | 
			
		||||
    /** 逆时针 范例:G17 G03 X150 Y50 I50 J0 F100(在XY平面,以半径50mm逆时针圆弧插补至X150、Y50)*/
 | 
			
		||||
    G3 = 'G3',
 | 
			
		||||
    /** 逆时针 范例:G17 G03 X150 Y50 I50 J0 F100(在XY平面,以半径50mm逆时针圆弧插补至X150、Y50)*/
 | 
			
		||||
    G03 = 'G03',
 | 
			
		||||
    /**暂停 范例:G04 X2(暂停2秒)*/
 | 
			
		||||
    G4 = 'G4',
 | 
			
		||||
    /**暂停 范例:G04 X2(暂停2秒)*/
 | 
			
		||||
    G04 = 'G04',
 | 
			
		||||
    /** 通过中间点圆弧插补 
 | 
			
		||||
     * 1. 通过中间点圆弧插补
 | 
			
		||||
     * 示例:G05 X60 Z50 IX50 IZ60 F120 刀具从当前位置出发,经过中间点 (50, 60),最终到达终点 (60, 50),形成圆弧轨迹。
 | 
			
		||||
     * 2. 高精轨迹控制(HPCC)模式
 | 
			
		||||
     * 功能:G05 可启动高精轨迹控制模式(HPCC),通过曲线拟合和参数优化,实现高精度、高速度的轨迹加工,适用于复杂曲面或高精度零件。
 | 
			
		||||
     * 指令格式:
 | 
			
		||||
     * 启动:G05 P10000(P值设为10000)
 | 
			
		||||
     * 关闭:G05 P0(P值设为0)
 | 
			
		||||
     * 3、注意事项:
 | 
			
		||||
     * 轴类型限制:建议线性轴参与HPCC模式,旋转轴需谨慎设置。
 | 
			
		||||
        与其他功能冲突:
 | 
			
		||||
        在HPCC模式下,G61(准确停止检查)、G63(攻螺纹)等功能可能失效,需退出HPCC模式后恢复。
 | 
			
		||||
        启用 RTCP(旋转刀具中心点)或 STCP 模式时,禁止同时启用HPCC功能。
 | 
			
		||||
        不支持的功能:
 | 
			
		||||
        不支持单节停止(C40/M00)、反向手轮模拟、图形模拟等。
 | 
			
		||||
        剩余距离显示可能不准确,因显示的是离曲线终点的距离,而非单节终点。
 | 
			
		||||
     * */
 | 
			
		||||
    G5 = 'G5',
 | 
			
		||||
    /** 通过中间点圆弧插补 */
 | 
			
		||||
    G05 = 'G05',
 | 
			
		||||
    /** 抛物线插补 */
 | 
			
		||||
    G6 = 'G6',
 | 
			
		||||
    /** 抛物线插补 */
 | 
			
		||||
    G06 = 'G06',
 | 
			
		||||
    /** z样条曲线插补 */
 | 
			
		||||
    G7 = 'G7',
 | 
			
		||||
    /** z样条曲线插补 */
 | 
			
		||||
    G07 = 'G07',
 | 
			
		||||
    /** 进给加速 */
 | 
			
		||||
    G8 = 'G8',
 | 
			
		||||
    /** 进给加速 */
 | 
			
		||||
    G08 = 'G08',
 | 
			
		||||
    /** 进给减速 */
 | 
			
		||||
    G9 = 'G9',
 | 
			
		||||
    /** 进给减速 */
 | 
			
		||||
    G09 = 'G09',
 | 
			
		||||
    /** 参数写入方式有效 */
 | 
			
		||||
    G10 = 'G10',
 | 
			
		||||
    /** 参数写入方式取消 */
 | 
			
		||||
    G11 = 'G11',
 | 
			
		||||
    /** 极坐标变成 */
 | 
			
		||||
    G16 = 'G16',
 | 
			
		||||
    /** XY平面选择 */
 | 
			
		||||
    G17 = 'G17',
 | 
			
		||||
    /** XZ平面选择 */
 | 
			
		||||
    G18 = 'G18',
 | 
			
		||||
    /** YZ平面选择 */
 | 
			
		||||
    G19 = 'G19',
 | 
			
		||||
    /** 英制输入 */
 | 
			
		||||
    G20 = 'G20',
 | 
			
		||||
    /** 公制输入 */
 | 
			
		||||
    G21 = 'G21',
 | 
			
		||||
    /** 半径尺寸编程方式  */
 | 
			
		||||
    G22 = 'G22',
 | 
			
		||||
    /** 系统操作界面上使用 */
 | 
			
		||||
    G220 = 'G220',
 | 
			
		||||
    /** 直径尺寸编程方式 */
 | 
			
		||||
    G23 = 'G23',
 | 
			
		||||
    /** 系统操作界面上使用  */
 | 
			
		||||
    G230 = 'G230',
 | 
			
		||||
    /** 子程序结束  */
 | 
			
		||||
    G24 = 'G24',
 | 
			
		||||
    /** 跳转加工  */
 | 
			
		||||
    G25 = 'G25',
 | 
			
		||||
    /** 循环加工 */
 | 
			
		||||
    G26 = 'G26',
 | 
			
		||||
    /** 参考点返回 */
 | 
			
		||||
    G28 = 'G28',
 | 
			
		||||
    /** 倍率注销 */
 | 
			
		||||
    G30 = 'G30',
 | 
			
		||||
    /** 倍率定义 */
 | 
			
		||||
    G31 = 'G31',
 | 
			
		||||
    /** 等螺距螺纹切削,英制 */
 | 
			
		||||
    G32 = 'G32',
 | 
			
		||||
    /** 等螺距螺纹切削,公制 */
 | 
			
		||||
    G33 = 'G33',
 | 
			
		||||
    /** 增螺距螺纹切削 */
 | 
			
		||||
    G34 = 'G34',
 | 
			
		||||
    /** 减螺距螺纹切削 */
 | 
			
		||||
    G35 = 'G35',
 | 
			
		||||
    //** 刀具半径补正取消 */
 | 
			
		||||
    G40 = 'G40',
 | 
			
		||||
    //** 刀具半径补正 左*/
 | 
			
		||||
    G41 = 'G41',
 | 
			
		||||
    //** 刀具半径补正 右*/
 | 
			
		||||
    G42 = 'G42',
 | 
			
		||||
    //** 刀具长度补正+ */
 | 
			
		||||
    G43 = 'G43',
 | 
			
		||||
    //** 刀具长度补正- */
 | 
			
		||||
    G44 = 'G44',
 | 
			
		||||
    //** 道具偏置+/+ */
 | 
			
		||||
    G45 = 'G45',
 | 
			
		||||
    //** 道具偏置+/- */
 | 
			
		||||
    G46 = 'G46',
 | 
			
		||||
    //** 道具偏置-/- */
 | 
			
		||||
    G47 = 'G47',
 | 
			
		||||
    //** 道具偏置-/+ */
 | 
			
		||||
    G48 = 'G48',
 | 
			
		||||
    //** 刀具长度 补正取消 */
 | 
			
		||||
    G49 = 'G49',
 | 
			
		||||
    //** 局部坐标系设定 */
 | 
			
		||||
    G52 = 'G52',
 | 
			
		||||
    //** 机床坐标系选择 */
 | 
			
		||||
    G53 = 'G53',
 | 
			
		||||
    //** 工件坐标系选择1 */
 | 
			
		||||
    G54 = 'G54',
 | 
			
		||||
    //** 工件坐标系选择2 */
 | 
			
		||||
    G55 = 'G55',
 | 
			
		||||
    //** 工件坐标系选择3 */
 | 
			
		||||
    G56 = 'G56',
 | 
			
		||||
    //** 工件坐标系选择4 */
 | 
			
		||||
    G57 = 'G57',
 | 
			
		||||
    //** 工件坐标系选择5 */
 | 
			
		||||
    G58 = 'G58',
 | 
			
		||||
    //** 工件坐标系选择6 */
 | 
			
		||||
    G59 = 'G59',
 | 
			
		||||
    //** 坐标系旋转有效 */
 | 
			
		||||
    G68 = 'G68',
 | 
			
		||||
    //** 坐标系旋转取消 */
 | 
			
		||||
    G69 = 'G69',
 | 
			
		||||
    /** 高速深孔钻 */
 | 
			
		||||
    G73 = 'G73',
 | 
			
		||||
    /** 精搪孔 */
 | 
			
		||||
    G76 = 'G76',
 | 
			
		||||
    /** 固定循环取消/取消循环指令 */
 | 
			
		||||
    G80 = 'G80',
 | 
			
		||||
    /**程序停止 */
 | 
			
		||||
    M00 = 'M00',
 | 
			
		||||
    /**选择性停止 */
 | 
			
		||||
    M01 = 'M01',
 | 
			
		||||
    /** 程序结束 */
 | 
			
		||||
    M02 = 'M02',
 | 
			
		||||
    /**主轴正转 */
 | 
			
		||||
    M03 = 'M03',
 | 
			
		||||
    /**主轴反转 */
 | 
			
		||||
    M04 = 'M04',
 | 
			
		||||
    /**主轴停止 */
 | 
			
		||||
    M05 = 'M05',
 | 
			
		||||
    /**自动换刀 */
 | 
			
		||||
    M06 = 'M06',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**G代码基类 */
 | 
			
		||||
export class GCodeAction {
 | 
			
		||||
 | 
			
		||||
    /** 空程直线 */
 | 
			
		||||
    G0(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, f, xKey, yKey, zKey, fKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G0
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 空程直线 */
 | 
			
		||||
    G00(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, f, xKey, yKey, zKey, fKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G00
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 直线 */
 | 
			
		||||
    G1(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, f, xKey, yKey, zKey, fKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G1
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 直线 */
 | 
			
		||||
    G01(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, f, xKey, yKey, zKey, fKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G01
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 顺时针 弧线 
 | 
			
		||||
     * @param x  x坐标
 | 
			
		||||
     * @param y  y坐标
 | 
			
		||||
     * @param z  z坐标
 | 
			
		||||
     * @param i  圆弧 对于起点的偏移量 x
 | 
			
		||||
     * @param y  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param z  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param f  速度
 | 
			
		||||
    */
 | 
			
		||||
    G2(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, i, j, k, r, f, xKey, yKey, zKey, fKey, iKey, jKey, kKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G2
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
        let _iKey = iKey || 'I'
 | 
			
		||||
        let _jKey = jKey || 'J'
 | 
			
		||||
        let _kKey = kKey || 'K'
 | 
			
		||||
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(r)) {
 | 
			
		||||
            val += ` R${r}`
 | 
			
		||||
        } else {
 | 
			
		||||
            if (checkVal(i)) {
 | 
			
		||||
                val += ` ${_iKey}${i}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(j)) {
 | 
			
		||||
                val += ` ${_jKey}${j}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(k)) {
 | 
			
		||||
                val += ` ${_kKey}${k}`
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 顺时针 弧线 
 | 
			
		||||
     * @param x  x坐标
 | 
			
		||||
     * @param y  y坐标
 | 
			
		||||
     * @param z  z坐标
 | 
			
		||||
     * @param i  圆弧 对于起点的偏移量 x
 | 
			
		||||
     * @param y  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param z  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param f  速度
 | 
			
		||||
    */
 | 
			
		||||
    G02(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, i, j, k, r, f, xKey, yKey, zKey, fKey, iKey, jKey, kKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G02
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
        let _iKey = iKey || 'I'
 | 
			
		||||
        let _jKey = jKey || 'J'
 | 
			
		||||
        let _kKey = kKey || 'K'
 | 
			
		||||
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (checkVal(r)) {
 | 
			
		||||
            val += ` R${r}`
 | 
			
		||||
        } else {
 | 
			
		||||
            if (checkVal(i)) {
 | 
			
		||||
                val += ` ${_iKey}${i}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(j)) {
 | 
			
		||||
                val += ` ${_jKey}${j}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(k)) {
 | 
			
		||||
                val += ` ${_kKey}${k}`
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 逆时针 弧线 
 | 
			
		||||
     * @param x  x坐标
 | 
			
		||||
     * @param y  y坐标
 | 
			
		||||
     * @param z  z坐标
 | 
			
		||||
     * @param i  圆弧 对于起点的偏移量 x
 | 
			
		||||
     * @param y  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param z  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param f  速度
 | 
			
		||||
    */
 | 
			
		||||
    G3(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, i, j, k, r, f, xKey, yKey, zKey, fKey, iKey, jKey, kKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G3
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
        let _iKey = iKey || 'I'
 | 
			
		||||
        let _jKey = jKey || 'J'
 | 
			
		||||
        let _kKey = kKey || 'K'
 | 
			
		||||
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (checkVal(r)) {
 | 
			
		||||
            val += ` R${r}`
 | 
			
		||||
        } else {
 | 
			
		||||
            if (checkVal(i)) {
 | 
			
		||||
                val += ` ${_iKey}${i}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(j)) {
 | 
			
		||||
                val += ` ${_jKey}${j}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(k)) {
 | 
			
		||||
                val += ` ${_kKey}${k}`
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 逆时针 弧线 
 | 
			
		||||
     * @param x  x坐标
 | 
			
		||||
     * @param y  y坐标
 | 
			
		||||
     * @param z  z坐标
 | 
			
		||||
     * @param i  圆弧 对于起点的偏移量 x
 | 
			
		||||
     * @param y  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param z  圆弧 对于起点的偏移量 Y
 | 
			
		||||
     * @param f  速度
 | 
			
		||||
    */
 | 
			
		||||
    G03(params: CodeParams) {
 | 
			
		||||
        const { x, y, z, i, j, k, r, f, xKey, yKey, zKey, fKey, iKey, jKey, kKey, codeKey } = params
 | 
			
		||||
        let val: string = GCode.G03
 | 
			
		||||
        if (typeof (codeKey) == 'string' && codeKey != '') {
 | 
			
		||||
            val = codeKey
 | 
			
		||||
        }
 | 
			
		||||
        let _xkey = xKey || 'X'
 | 
			
		||||
        let _yKey = yKey || 'Y'
 | 
			
		||||
        let _zKey = zKey || 'Z'
 | 
			
		||||
        let _fKey = fKey || 'F'
 | 
			
		||||
        let _iKey = iKey || 'I'
 | 
			
		||||
        let _jKey = jKey || 'J'
 | 
			
		||||
        let _kKey = kKey || 'K'
 | 
			
		||||
 | 
			
		||||
        if (checkVal(x)) {
 | 
			
		||||
            val += ` ${_xkey}${x}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(y)) {
 | 
			
		||||
            val += ` ${_yKey}${y}`
 | 
			
		||||
        }
 | 
			
		||||
        if (checkVal(z)) {
 | 
			
		||||
            val += ` ${_zKey}${z}`
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (checkVal(r)) {
 | 
			
		||||
            val += ` R${r}`
 | 
			
		||||
        } else {
 | 
			
		||||
            if (checkVal(i)) {
 | 
			
		||||
                val += ` ${_iKey}${i}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(j)) {
 | 
			
		||||
                val += ` ${_jKey}${j}`
 | 
			
		||||
            }
 | 
			
		||||
            if (checkVal(k)) {
 | 
			
		||||
                val += ` ${_kKey}${k}`
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (checkVal(f)) {
 | 
			
		||||
            val += ` ${_fKey}${f}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /** 主轴正转 
 | 
			
		||||
     *  
 | 
			
		||||
     * @param s 转速
 | 
			
		||||
    */
 | 
			
		||||
    M03(s: any) {
 | 
			
		||||
        let val: string = GCode.M03
 | 
			
		||||
        if (checkVal(s)) {
 | 
			
		||||
            val += ` S${s}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /**主轴反向转 
 | 
			
		||||
     * @param s 转速
 | 
			
		||||
    */
 | 
			
		||||
    M04(s: any) {
 | 
			
		||||
        let val: string = GCode.M04
 | 
			
		||||
        if (checkVal(s)) {
 | 
			
		||||
            val += ` S${s}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**主轴停止 */
 | 
			
		||||
    M05() {
 | 
			
		||||
        let val: string = GCode.M05
 | 
			
		||||
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
    /**换刀指令 */
 | 
			
		||||
    M06(t: String) {
 | 
			
		||||
        let val: string = GCode.M06
 | 
			
		||||
        if (checkVal(t)) {
 | 
			
		||||
            val += ` T${t}`
 | 
			
		||||
        }
 | 
			
		||||
        return val
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1746
									
								
								samples/handleAbility/RectOptimizeWorker/RectOptimizeMachine.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1746
									
								
								samples/handleAbility/RectOptimizeWorker/RectOptimizeMachine.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
import type { Big_bang, xbang } from './bang'
 | 
			
		||||
import { RectOptimizeMachine } from './RectOptimizeMachine'
 | 
			
		||||
import {Worker} from "worker_threads"
 | 
			
		||||
 | 
			
		||||
const ctx: Worker = self as any
 | 
			
		||||
 
 | 
			
		||||
ctx.addListener('message', async (event) => {
 | 
			
		||||
  let m = new RectOptimizeMachine()
 | 
			
		||||
  m.CallBack = async (best, fit, arg, info) => {
 | 
			
		||||
 | 
			
		||||
    ctx.postMessage([best, fit, arg, info])
 | 
			
		||||
  }
 | 
			
		||||
  if (event.data.type == 'start') {
 | 
			
		||||
    /**
 | 
			
		||||
     * blockList 小板列表
 | 
			
		||||
     * boardList 大板(N个元素,前N-1个元素表示余料板且余料板须为矩形,第N个元素表示大板)
 | 
			
		||||
     * boardCount 余料板数量(bigBang中前N-1个元素对应的数量,如果bigBang中只有一个元素即只有大板没有余料板,则为空数组)
 | 
			
		||||
     * optimizeTimes 新优化次数
 | 
			
		||||
     * isDoubleFaceBlockFirst 双面加工的小板是否优先排入
 | 
			
		||||
     * gap 排版缝隙 = 开料刀直径 + 缝隙
 | 
			
		||||
     * gzpb 规则排版
 | 
			
		||||
     * isDoubleFaceBlockInRemain 余料板是否排入双面加工的小板
 | 
			
		||||
     */
 | 
			
		||||
    let [blockList, boardList, boardCount, optimizeTimes, isDoubleFaceBlockFirst, gap, gzpb, isDoubleFaceBlockInRemain] = (event.data.data) as [xbang[], Big_bang[], number[], number, boolean, number, boolean, boolean]
 | 
			
		||||
 | 
			
		||||
    m.Start(blockList, boardList, boardCount, optimizeTimes, isDoubleFaceBlockFirst, gap, gzpb, isDoubleFaceBlockInRemain)
 | 
			
		||||
  } else {
 | 
			
		||||
    const info = {
 | 
			
		||||
      type: 'isStop',
 | 
			
		||||
    }
 | 
			
		||||
    await m.Stop(info)
 | 
			
		||||
    ctx.postMessage([[], null, null, info])
 | 
			
		||||
    ctx?.terminate()
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
// ctx.addEventListener('message', async (event) => {
 | 
			
		||||
//   let m = new RectOptimizeMachine()
 | 
			
		||||
//   m.CallBack = async (best, fit, arg, info) => {
 | 
			
		||||
 | 
			
		||||
//     ctx.postMessage([best, fit, arg, info])
 | 
			
		||||
//   }
 | 
			
		||||
//   if (event.data.type == 'start') {
 | 
			
		||||
//     /**
 | 
			
		||||
//      * blockList 小板列表
 | 
			
		||||
//      * boardList 大板(N个元素,前N-1个元素表示余料板且余料板须为矩形,第N个元素表示大板)
 | 
			
		||||
//      * boardCount 余料板数量(bigBang中前N-1个元素对应的数量,如果bigBang中只有一个元素即只有大板没有余料板,则为空数组)
 | 
			
		||||
//      * optimizeTimes 新优化次数
 | 
			
		||||
//      * isDoubleFaceBlockFirst 双面加工的小板是否优先排入
 | 
			
		||||
//      * gap 排版缝隙 = 开料刀直径 + 缝隙
 | 
			
		||||
//      * gzpb 规则排版
 | 
			
		||||
//      * isDoubleFaceBlockInRemain 余料板是否排入双面加工的小板
 | 
			
		||||
//      */
 | 
			
		||||
//     let [blockList, boardList, boardCount, optimizeTimes, isDoubleFaceBlockFirst, gap, gzpb, isDoubleFaceBlockInRemain] = (event.data.data) as [xbang[], Big_bang[], number[], number, boolean, number, boolean, boolean]
 | 
			
		||||
 | 
			
		||||
//     m.Start(blockList, boardList, boardCount, optimizeTimes, isDoubleFaceBlockFirst, gap, gzpb, isDoubleFaceBlockInRemain)
 | 
			
		||||
//   } else {
 | 
			
		||||
//     const info = {
 | 
			
		||||
//       type: 'isStop',
 | 
			
		||||
//     }
 | 
			
		||||
//     await m.Stop(info)
 | 
			
		||||
//     ctx.postMessage([[], null, null, info])
 | 
			
		||||
//     ctx?.terminate()
 | 
			
		||||
//   }
 | 
			
		||||
// })
 | 
			
		||||
 | 
			
		||||
export default {} as typeof Worker & (new () => Worker)
 | 
			
		||||
							
								
								
									
										115
									
								
								samples/handleAbility/RectOptimizeWorker/bang.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								samples/handleAbility/RectOptimizeWorker/bang.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
 | 
			
		||||
export type Con = YH_bang[];//单块1220*2440的板结果 Container
 | 
			
		||||
export type Inv = Con[];    //个体:优化结果 包含多个大板 Individual
 | 
			
		||||
 | 
			
		||||
/**纹路类型 Positive=0正纹 Reverse=1反纹 CanReversal=2可翻转 */
 | 
			
		||||
export enum LineType
 | 
			
		||||
{
 | 
			
		||||
    /**正纹 */
 | 
			
		||||
    Positive = 0,
 | 
			
		||||
    /**反纹 */
 | 
			
		||||
    Reverse = 1,
 | 
			
		||||
    /**可翻转 */
 | 
			
		||||
    CanReversal = 2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**优化的大板 */
 | 
			
		||||
export interface YH_bang
 | 
			
		||||
{
 | 
			
		||||
    /**板ID */
 | 
			
		||||
    bangid: number;
 | 
			
		||||
    /**纹路 */
 | 
			
		||||
    line: LineType;
 | 
			
		||||
    x: number;
 | 
			
		||||
    y: number;
 | 
			
		||||
    /**排版高 */
 | 
			
		||||
    pbg: number;
 | 
			
		||||
    /**排版宽 */
 | 
			
		||||
    pbk: number;
 | 
			
		||||
 | 
			
		||||
    ishb?: boolean;//是否参与合并的板
 | 
			
		||||
    hb?: number[];  //合在并的板
 | 
			
		||||
    isgr?: boolean; //是否关连
 | 
			
		||||
    gr?: number[];//关联的板的集合
 | 
			
		||||
    grid?: number; //跟别的板关联的ID
 | 
			
		||||
    isbig?: boolean;//是否为合并的大板
 | 
			
		||||
    isqg?: boolean;//是否被切掉的板
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**版面类型: Positive=0正面 Reverse=1反面 Arbitrary=2任意面 */
 | 
			
		||||
export enum ComposingType
 | 
			
		||||
{
 | 
			
		||||
    /**正面 */
 | 
			
		||||
    Positive = 0,
 | 
			
		||||
    /**反面 */
 | 
			
		||||
    Reverse = 1,
 | 
			
		||||
    /**任意面 */
 | 
			
		||||
    Arbitrary = 2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**孔类型 None=0无 Positive=1正面 Reverse=2反面 Two=3正反 */
 | 
			
		||||
export enum HoleType
 | 
			
		||||
{
 | 
			
		||||
    /**无 */
 | 
			
		||||
    None = 0,
 | 
			
		||||
    /**正面 */
 | 
			
		||||
    Positive = 1,
 | 
			
		||||
    /**反面 */
 | 
			
		||||
    Reverse = 2,
 | 
			
		||||
    /**正反 */
 | 
			
		||||
    Two = 3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 小板 */
 | 
			
		||||
export interface xbang
 | 
			
		||||
{
 | 
			
		||||
    /**长 */
 | 
			
		||||
    l: number;
 | 
			
		||||
    /**宽 */
 | 
			
		||||
    w: number;
 | 
			
		||||
    /**纹路 */
 | 
			
		||||
    line: LineType;
 | 
			
		||||
    /**排版面 */
 | 
			
		||||
    face: ComposingType;
 | 
			
		||||
    /**小板ID */
 | 
			
		||||
    id: number;
 | 
			
		||||
    /**小板编号 */
 | 
			
		||||
    bno: string;
 | 
			
		||||
    /**孔面: 0无孔 1正面有孔 2反面有孔 3正反面都有孔 */
 | 
			
		||||
    holeFaceCount: HoleType;
 | 
			
		||||
    /**是矩形 */
 | 
			
		||||
    isRect?: boolean;
 | 
			
		||||
    /**有孔 */
 | 
			
		||||
    hasHole?: boolean;
 | 
			
		||||
    /**false单面  true双面 */
 | 
			
		||||
    isdtwosided?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 大板 */
 | 
			
		||||
export interface Big_bang   //待优化的板
 | 
			
		||||
{
 | 
			
		||||
    l: number;              //长
 | 
			
		||||
    w: number;              //宽
 | 
			
		||||
    x: number;              //x
 | 
			
		||||
    y: number;              //y
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export enum BlockRegion
 | 
			
		||||
  {
 | 
			
		||||
  /** 左下 = 0 */
 | 
			
		||||
  LEFT_BOTTOM = 0,
 | 
			
		||||
  /** 右下 = 1 */
 | 
			
		||||
  RIGHT_BOTTOM = 1,
 | 
			
		||||
  /** 右上 = 2 */
 | 
			
		||||
  RIGHT_TOP = 2,
 | 
			
		||||
  /** 左上 = 3 */
 | 
			
		||||
  LEFT_TOP = 3,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class WorkerItemType {
 | 
			
		||||
    w?: Worker
 | 
			
		||||
    goodsId?: string | number
 | 
			
		||||
    pm?: any
 | 
			
		||||
    status?: 'start' | 'stop'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								samples/handleAbility/RectOptimizeWorker/新优化.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								samples/handleAbility/RectOptimizeWorker/新优化.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
# 新优化
 | 
			
		||||
							
								
								
									
										13
									
								
								samples/handleAbility/ability_1.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								samples/handleAbility/ability_1.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import { ConfigBase } from "../../src/models/config";
 | 
			
		||||
 | 
			
		||||
/** 基础功能1 -- 新优化 */
 | 
			
		||||
export class BlockPlace extends ConfigBase{
 | 
			
		||||
    // 优化过程中的回调函数
 | 
			
		||||
    callBack?:Function 
 | 
			
		||||
    constructor(){
 | 
			
		||||
        super()
 | 
			
		||||
        this.name = 'XXX'
 | 
			
		||||
        this.version = '20250707'
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										132
									
								
								samples/handleAbility/blockDetailHelperBase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								samples/handleAbility/blockDetailHelperBase.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
import { FaceType, PlaceBlock, PlaceBlockDetail, PlaceBorderContour, PlaceMaterial, PlaceStyle, SizeExpand } from "../confClass"
 | 
			
		||||
import { Arc2d, Curve2d, Line2d } from "./common/base/CAD"
 | 
			
		||||
import { PolylineHelper } from "./common/LayoutEngine/PolylineHelper"
 | 
			
		||||
import { KnifeHelper } from "./knifeHelper"
 | 
			
		||||
 | 
			
		||||
/** 小板明细 相关的计算  孔 造型 以及轮廓 */
 | 
			
		||||
export class BlockDetailHelperBase {
 | 
			
		||||
 | 
			
		||||
    /** 造型轮廓(含封边),扣除封边, 变成开料坐标 */
 | 
			
		||||
    resetModelContour(bd: PlaceBlockDetail) {
 | 
			
		||||
        let ox = bd.offsetX
 | 
			
		||||
        let oy = bd.offsetY
 | 
			
		||||
        for (let m of bd.models) {
 | 
			
		||||
            if (m.hasContour()) {
 | 
			
		||||
                let ptsArr = m.originModeling.outline.map(e => e.pts)
 | 
			
		||||
                for (let pt of ptsArr) {
 | 
			
		||||
                    // 23.8.5 发现矩形的挖穿轮廓坐标是不含封边的
 | 
			
		||||
                    pt.x -= ox
 | 
			
		||||
                    pt.y -= oy
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // 原始轮廓,成品轮廓
 | 
			
		||||
    createFinalBorder(bd: PlaceBlockDetail, block: PlaceBlock): Curve2d[] {
 | 
			
		||||
        const orgPoints = bd.orgPoints
 | 
			
		||||
        const orgBorderCurveList = new Array<Curve2d>()
 | 
			
		||||
        if (orgPoints && orgPoints.length > 1) // 异形
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            const count = orgPoints.length
 | 
			
		||||
            for (let i = 0; i < count; i++) {
 | 
			
		||||
                const p0 = orgPoints[i]
 | 
			
		||||
                const p1 = i == count - 1 ? orgPoints[0] : orgPoints[i + 1]
 | 
			
		||||
                const sideHoleCount = bd.holeListSide.filter(t => t.faceId == i).length
 | 
			
		||||
                if (p0.curve == 0) // 直线
 | 
			
		||||
                {
 | 
			
		||||
                    const newLine = Line2d.New(p0.pointX, p0.pointY, p1.pointX, p1.pointY)
 | 
			
		||||
                    newLine.tagData = p0.sealSize
 | 
			
		||||
                    newLine.tagData2 = sideHoleCount
 | 
			
		||||
                    orgBorderCurveList.push(newLine)
 | 
			
		||||
                    p0.radius = 0
 | 
			
		||||
                }
 | 
			
		||||
                else // 曲线
 | 
			
		||||
                {
 | 
			
		||||
                    const crc = Arc2d.New(p0.pointX, p0.pointY, p1.pointX, p1.pointY, p0.curve)
 | 
			
		||||
                    crc.tagData = p0.sealSize
 | 
			
		||||
                    crc.tagData2 = sideHoleCount
 | 
			
		||||
                    p0.radius = crc.m_Radius
 | 
			
		||||
                    orgBorderCurveList.push(crc)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else // 矩形板
 | 
			
		||||
        {
 | 
			
		||||
            const w = block.width
 | 
			
		||||
            const l = block.length
 | 
			
		||||
 | 
			
		||||
            const line0 = Line2d.New(0, 0, w, 0)
 | 
			
		||||
            line0.tagData = block.sealBottom
 | 
			
		||||
            line0.tagData2 = block.holeCountBottom() || 0
 | 
			
		||||
 | 
			
		||||
            const line1 = Line2d.New(w, 0, w, l)
 | 
			
		||||
            line1.tagData = block.sealRight
 | 
			
		||||
            line1.tagData2 = block.holeCountRight() || 0
 | 
			
		||||
 | 
			
		||||
            const line2 = Line2d.New(w, l, 0, l)
 | 
			
		||||
            line2.tagData = block.sealTop
 | 
			
		||||
            line2.tagData2 = block.holeCountTop() || 0
 | 
			
		||||
 | 
			
		||||
            const line3 = Line2d.New(0, l, 0, 0)
 | 
			
		||||
            line3.tagData = block.sealLeft
 | 
			
		||||
            line3.tagData2 = block.holeCountLeft() || 0
 | 
			
		||||
 | 
			
		||||
            orgBorderCurveList.push(line0)
 | 
			
		||||
            orgBorderCurveList.push(line1)
 | 
			
		||||
            orgBorderCurveList.push(line2)
 | 
			
		||||
            orgBorderCurveList.push(line3)
 | 
			
		||||
        }
 | 
			
		||||
        return orgBorderCurveList
 | 
			
		||||
    }
 | 
			
		||||
    /** 创建 开料轮廓不含预铣 */
 | 
			
		||||
    createOrgBorder(bd: PlaceBlockDetail): Curve2d[] {
 | 
			
		||||
        const borders = new Array<Curve2d>()
 | 
			
		||||
        if (bd.points && bd.points.length > 1) // 异形
 | 
			
		||||
        {
 | 
			
		||||
            const count = bd.points.length
 | 
			
		||||
            for (let i = 0; i < count - 1; i++) // 异形点(无封边,起点终点 是重复的)
 | 
			
		||||
            {
 | 
			
		||||
                const p0 = bd.points[i]
 | 
			
		||||
                const p1 = i == count - 1 ? bd.points[0] : bd.points[i + 1]
 | 
			
		||||
                const sideHoleCount = bd.holeListSide.filter(t => t.faceId == i).length
 | 
			
		||||
                if (p0.curve == 0) // 直线
 | 
			
		||||
                {
 | 
			
		||||
                    const newLine = Line2d.New(p0.pointX, p0.pointY, p1.pointX, p1.pointY)
 | 
			
		||||
                    newLine.tagData = p0.sealSize
 | 
			
		||||
                    newLine.tagData2 = sideHoleCount
 | 
			
		||||
                    borders.push(newLine)
 | 
			
		||||
                    p0.radius = 0
 | 
			
		||||
                }
 | 
			
		||||
                else // 曲线
 | 
			
		||||
                {
 | 
			
		||||
                    const crc = Arc2d.New(p0.pointX, p0.pointY, p1.pointX, p1.pointY, p0.curve)
 | 
			
		||||
                    crc.tagData = p0.sealSize
 | 
			
		||||
                    crc.tagData2 = sideHoleCount
 | 
			
		||||
                    p0.radius = crc.m_Radius
 | 
			
		||||
                    if (p0.radius < 2) {
 | 
			
		||||
                        p0.curve = 0
 | 
			
		||||
                        p0.radius = 0
 | 
			
		||||
                    }
 | 
			
		||||
                    borders.push(crc)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else // 矩形板
 | 
			
		||||
        {
 | 
			
		||||
            const w = bd.cutWidth
 | 
			
		||||
            const l = bd.cutLength
 | 
			
		||||
            const line0 = Line2d.New(0, 0, w, 0)
 | 
			
		||||
            const line1 = Line2d.New(w, 0, w, l)
 | 
			
		||||
            const line2 = Line2d.New(w, l, 0, l)
 | 
			
		||||
            const line3 = Line2d.New(0, l, 0, 0)
 | 
			
		||||
 | 
			
		||||
            borders.push(line0)
 | 
			
		||||
            borders.push(line1)
 | 
			
		||||
            borders.push(line2)
 | 
			
		||||
            borders.push(line3)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return borders
 | 
			
		||||
    }
 | 
			
		||||
}  
 | 
			
		||||
							
								
								
									
										4
									
								
								samples/handleAbility/blockHelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								samples/handleAbility/blockHelper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
/** 小板相关的计算 */
 | 
			
		||||
export class BlockHelper{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										392
									
								
								samples/handleAbility/common/ArrayExt.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								samples/handleAbility/common/ArrayExt.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,392 @@
 | 
			
		||||
export class List<Item> extends Array<Item>
 | 
			
		||||
{
 | 
			
		||||
  /** 返回符合条件的第一个元素 */
 | 
			
		||||
  first(fn: (item: Item) => boolean): Item
 | 
			
		||||
  {
 | 
			
		||||
    if (this.length == 0)
 | 
			
		||||
      return null
 | 
			
		||||
    for (const item of this)
 | 
			
		||||
    {
 | 
			
		||||
      if (fn(item))
 | 
			
		||||
        return item
 | 
			
		||||
    }
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 返回符合条件的最后一元素 */
 | 
			
		||||
  last(fn: (t: Item) => boolean): Item
 | 
			
		||||
  {
 | 
			
		||||
    if (this.length == 0)
 | 
			
		||||
      return null
 | 
			
		||||
    for (let i = this.length - 1; i >= 0; i--)
 | 
			
		||||
    {
 | 
			
		||||
      if (fn(this[i]))
 | 
			
		||||
        return this[i]
 | 
			
		||||
    }
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 最大值 */
 | 
			
		||||
  max<T>(fn: (item: Item) => T): T
 | 
			
		||||
  {
 | 
			
		||||
    let maxV: T
 | 
			
		||||
    for (const i of this)
 | 
			
		||||
    {
 | 
			
		||||
      let v = fn(i)
 | 
			
		||||
      if (maxV == null)
 | 
			
		||||
      {
 | 
			
		||||
        maxV = fn(i)
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        if (v > maxV)
 | 
			
		||||
        {
 | 
			
		||||
          maxV = v
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return maxV
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 最小值 */
 | 
			
		||||
  min<T>(fn: (item: Item) => T): T
 | 
			
		||||
  {
 | 
			
		||||
    let minV: T
 | 
			
		||||
    for (const i of this)
 | 
			
		||||
    {
 | 
			
		||||
      let v = fn(i)
 | 
			
		||||
      if (minV == null)
 | 
			
		||||
      {
 | 
			
		||||
        minV = fn(i)
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        if (v < minV)
 | 
			
		||||
        {
 | 
			
		||||
          minV = v
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return minV
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 累加 */
 | 
			
		||||
  sum(fn: (item: Item) => number): number
 | 
			
		||||
  {
 | 
			
		||||
    let v: number = 0
 | 
			
		||||
    for (const t of this)
 | 
			
		||||
    {
 | 
			
		||||
      v = v + fn(t)
 | 
			
		||||
    }
 | 
			
		||||
    return v
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 平均值 */
 | 
			
		||||
  avg(fn: (item: Item) => number): number
 | 
			
		||||
  {
 | 
			
		||||
    if (this.length == 0)
 | 
			
		||||
      return 0
 | 
			
		||||
    let sum = this.sum(fn)
 | 
			
		||||
    return sum / this.length
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 满足条件的元素数量 */
 | 
			
		||||
  count(fn: (item: Item) => number): number
 | 
			
		||||
  {
 | 
			
		||||
    if (this.length == 0)
 | 
			
		||||
      return 0
 | 
			
		||||
    let c = 0
 | 
			
		||||
    for (const item of this)
 | 
			
		||||
    {
 | 
			
		||||
      if (fn(item))
 | 
			
		||||
        c = c + 1
 | 
			
		||||
    }
 | 
			
		||||
    return c
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FindMax<T>(fn: (item: Item) => T): Item
 | 
			
		||||
  {
 | 
			
		||||
    return this.reduce((a: Item, b: Item): Item => fn(a) > fn(b) ? a : b)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ArrayExt
 | 
			
		||||
{
 | 
			
		||||
  /** 返回满足条件的元素数量 */
 | 
			
		||||
  static count<Item>(list: Item[], fn: (item: Item) => boolean): number
 | 
			
		||||
  {
 | 
			
		||||
    if (list.length == 0)
 | 
			
		||||
      return 0
 | 
			
		||||
    let c = 0
 | 
			
		||||
    for (const item of list)
 | 
			
		||||
    {
 | 
			
		||||
      if (fn(item))
 | 
			
		||||
        c = c + 1
 | 
			
		||||
    }
 | 
			
		||||
    return c
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 移除 */
 | 
			
		||||
  static remove<Item>(list: Item[], obj: Item)
 | 
			
		||||
  {
 | 
			
		||||
    let index = list.findIndex(t => t == obj)
 | 
			
		||||
    if (index == -1)
 | 
			
		||||
      return
 | 
			
		||||
    list.splice(index, 1)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 返回符合条件的第一个元素 */
 | 
			
		||||
  static first<Item>(list: Item[], fn: (item: Item) => boolean, orderFn1: (item: Item) => number = null, orderFn2: (item: Item) => number = null): Item
 | 
			
		||||
  {
 | 
			
		||||
    if (list.length == 0)
 | 
			
		||||
      return null
 | 
			
		||||
    if (orderFn1 == null)
 | 
			
		||||
    {
 | 
			
		||||
      for (const item of list)
 | 
			
		||||
      {
 | 
			
		||||
        if (fn(item))
 | 
			
		||||
          return item
 | 
			
		||||
      }
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let minValue1: number
 | 
			
		||||
    let minValue2: number
 | 
			
		||||
    let minItem: Item
 | 
			
		||||
    for (const item of list)
 | 
			
		||||
    {
 | 
			
		||||
      if (fn(item) == false)
 | 
			
		||||
        continue
 | 
			
		||||
      let v1 = orderFn1(item)
 | 
			
		||||
      let v2 = orderFn2 != null ? orderFn2(item) : 0
 | 
			
		||||
      if (minValue1 == null || v1 < minValue1 || (v1 == minValue1 && v2 < minValue2))
 | 
			
		||||
      {
 | 
			
		||||
        minValue1 = v1
 | 
			
		||||
        minValue2 = v2
 | 
			
		||||
        minItem = item
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return minItem
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 返回符合条件的最后一元素 */
 | 
			
		||||
  static last<Item>(list: Item[], fn: (t: Item) => boolean, orderFn1: (item: Item) => number = null, orderFn2: (item: Item) => number = null): Item
 | 
			
		||||
  {
 | 
			
		||||
    if (list.length == 0)
 | 
			
		||||
      return null
 | 
			
		||||
    if (orderFn1 == null)
 | 
			
		||||
    {
 | 
			
		||||
      for (let i = list.length - 1; i >= 0; i--)
 | 
			
		||||
      {
 | 
			
		||||
        if (fn(list[i]))
 | 
			
		||||
          return list[i]
 | 
			
		||||
      }
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    let maxValue1: number
 | 
			
		||||
    let maxValue2: number
 | 
			
		||||
    let maxItem: Item
 | 
			
		||||
    for (const item of list)
 | 
			
		||||
    {
 | 
			
		||||
      if (fn(item) == false)
 | 
			
		||||
        continue
 | 
			
		||||
      let v1 = orderFn1(item)
 | 
			
		||||
      let v2 = orderFn2 ? orderFn2(item) : 0
 | 
			
		||||
      if (maxValue1 == null || v1 > maxValue1 || (v1 == maxValue1 && v2 > maxValue2))
 | 
			
		||||
      {
 | 
			
		||||
        maxValue1 = v1
 | 
			
		||||
        maxValue2 = v2
 | 
			
		||||
        maxItem = item
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return maxItem
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 取最大值 */
 | 
			
		||||
  static max<T1, T2>(list: T1[], fn: (item: T1) => T2, whereF: (item: T1) => boolean = null, defaultV: T2 = null): T2
 | 
			
		||||
  {
 | 
			
		||||
    let maxV: T2
 | 
			
		||||
    for (const i of list)
 | 
			
		||||
    {
 | 
			
		||||
      if (whereF && whereF(i) == false)
 | 
			
		||||
        continue
 | 
			
		||||
      let v = fn(i)
 | 
			
		||||
      if (maxV == undefined)
 | 
			
		||||
      {
 | 
			
		||||
        maxV = fn(i)
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        if (v > maxV)
 | 
			
		||||
        {
 | 
			
		||||
          maxV = v
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (maxV != undefined)
 | 
			
		||||
      return maxV
 | 
			
		||||
    return defaultV
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 最小值 */
 | 
			
		||||
  static min<Item, T>(list: Item[], fn: (item: Item) => T, whereF: (item: Item) => boolean = null, defaultV: T = null): T
 | 
			
		||||
  {
 | 
			
		||||
    let minV: T
 | 
			
		||||
    for (const i of list)
 | 
			
		||||
    {
 | 
			
		||||
      if (whereF && whereF(i) == false)
 | 
			
		||||
        continue
 | 
			
		||||
      let v = fn(i)
 | 
			
		||||
      if (minV == undefined)
 | 
			
		||||
      {
 | 
			
		||||
        minV = v
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        if (v < minV)
 | 
			
		||||
        {
 | 
			
		||||
          minV = v
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (minV != undefined)
 | 
			
		||||
      return minV
 | 
			
		||||
    return defaultV
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 累加 */
 | 
			
		||||
  static sum<Item>(list: Item[], fn: (item: Item) => number, wn?: (item: Item) => boolean): number
 | 
			
		||||
  {
 | 
			
		||||
    let v: number = 0
 | 
			
		||||
    for (const t of list)
 | 
			
		||||
    {
 | 
			
		||||
      if (wn && wn(t) == false)
 | 
			
		||||
        continue
 | 
			
		||||
      v = v + fn(t)
 | 
			
		||||
    }
 | 
			
		||||
    return v
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 平均值 */
 | 
			
		||||
  static avg<Item>(list: Item[], fn: (item: Item) => number): number
 | 
			
		||||
  {
 | 
			
		||||
    if (this.length == 0)
 | 
			
		||||
      return 0
 | 
			
		||||
    let sum = ArrayExt.sum(list, fn)
 | 
			
		||||
    return sum / this.length
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 排序 */
 | 
			
		||||
  static sortBy<Item>(list: Item[], fn: (item: Item) => number, fn2: (item: Item) => number = null)
 | 
			
		||||
  {
 | 
			
		||||
    if (fn2 == null)
 | 
			
		||||
      return list.sort((a: Item, b: Item): number => fn(a) - fn(b))
 | 
			
		||||
    else
 | 
			
		||||
      return list.sort((a: Item, b: Item): number => fn(a) == fn(b) ? (fn2(a) - fn2(b)) : fn(a) - fn(b))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 降序 排序 */
 | 
			
		||||
  static sortByDescending<Item>(list: Item[], fn: (item: Item) => number)
 | 
			
		||||
  {
 | 
			
		||||
    list.sort((a: Item, b: Item): number => fn(b) - fn(a))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 排序成新的数组 */
 | 
			
		||||
  static orderBy<Item>(list: Item[], fn: (item: Item) => number, fn2: (item: Item) => number = null): Item[]
 | 
			
		||||
  {
 | 
			
		||||
    let newList = list.concat([])
 | 
			
		||||
    if (fn2 == null)
 | 
			
		||||
      return newList.sort((a: Item, b: Item): number => fn(a) - fn(b))
 | 
			
		||||
    else
 | 
			
		||||
      return newList.sort((a: Item, b: Item): number => fn(a) == fn(b) ? (fn2(a) - fn2(b)) : fn(a) - fn(b))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 降序成新的数组 */
 | 
			
		||||
  static orderByDescending<Item>(list: Item[], fn: (item: Item) => number, fn2: (item: Item) => number = null): Item[]
 | 
			
		||||
  {
 | 
			
		||||
    let newList = list.concat([])
 | 
			
		||||
    if (fn2 == null)
 | 
			
		||||
      return list.sort((a: Item, b: Item): number => fn(b) - fn(a))
 | 
			
		||||
    else
 | 
			
		||||
      return list.sort((a: Item, b: Item): number => fn(a) == fn(b) ? (fn2(b) - fn2(a)) : fn(b) - fn(a))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 分组 */
 | 
			
		||||
  static groupBy<Item, gT>(list: Item[], fn: (item: Item) => gT): GroupItem<gT, Item>[]
 | 
			
		||||
  {
 | 
			
		||||
    let groups = new Array<GroupItem<gT, Item>>()
 | 
			
		||||
 | 
			
		||||
    for (const item of list)
 | 
			
		||||
    {
 | 
			
		||||
      let key = fn(item)
 | 
			
		||||
      let group = groups.find(t => t.key == key)
 | 
			
		||||
      if (group == null)
 | 
			
		||||
      {
 | 
			
		||||
        group = new GroupItem(key)
 | 
			
		||||
        groups.push(group)
 | 
			
		||||
      }
 | 
			
		||||
      group.push(item)
 | 
			
		||||
    }
 | 
			
		||||
    return groups
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 选择
 | 
			
		||||
   * let newObjectList = ArrayExt.Select(list,t=>({pA:t.name, pB:t.age + "_"+ t.month}) ) ;
 | 
			
		||||
   */
 | 
			
		||||
  static Select<Item, rT>(list: Item[], fn: (item: Item) => rT): rT[]
 | 
			
		||||
  {
 | 
			
		||||
    let newList = new Array<rT>()
 | 
			
		||||
    for (const t of list)
 | 
			
		||||
    {
 | 
			
		||||
      newList.push(fn(t))
 | 
			
		||||
    }
 | 
			
		||||
    return newList
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 过来,并按顺序排序 */
 | 
			
		||||
  static where<Item>(list: Item[], whereFn: (item: Item) => boolean, orderfn1: (item: Item) => number = null, orderfn2: (item: Item) => number = null): Item[]
 | 
			
		||||
  {
 | 
			
		||||
    let newList = list.filter(whereFn)
 | 
			
		||||
    if (orderfn1 == null && orderfn2 == null)
 | 
			
		||||
      return newList
 | 
			
		||||
    return ArrayExt.sortBy(newList, orderfn1, orderfn2)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class GroupItem<gT, Item>
 | 
			
		||||
{
 | 
			
		||||
  key: gT
 | 
			
		||||
  get count() { return this.list.length }
 | 
			
		||||
  list: Item[]
 | 
			
		||||
 | 
			
		||||
  constructor(k: gT)
 | 
			
		||||
  {
 | 
			
		||||
    this.key = k
 | 
			
		||||
    this.list = []
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  push(d: Item)
 | 
			
		||||
  {
 | 
			
		||||
    this.list.push(d)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 对排序好的数组进行去重操作
 | 
			
		||||
 * @param {(e1, e2) => boolean} [checkFuction] 校验对象相等函数
 | 
			
		||||
 * @returns {Array<T>} 返回自身
 | 
			
		||||
 */
 | 
			
		||||
export function arrayRemoveDuplicateBySort<T>(arr: Array<T>, checkFuction: (e1: T, e2: T) => boolean = checkEqual): Array<T>
 | 
			
		||||
{
 | 
			
		||||
  if (arr.length < 2)
 | 
			
		||||
    return arr
 | 
			
		||||
  let j = 1
 | 
			
		||||
  for (let i = 1, l = arr.length; i < l; i++)
 | 
			
		||||
    if (!checkFuction(arr[j - 1], arr[i]))
 | 
			
		||||
      arr[j++] = arr[i]
 | 
			
		||||
  arr.length = j
 | 
			
		||||
  return arr
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										183
									
								
								samples/handleAbility/common/Box2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								samples/handleAbility/common/Box2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
			
		||||
import { Vector2 } from './Vector2.js'
 | 
			
		||||
import type { Point } from './Point.js'
 | 
			
		||||
 | 
			
		||||
export class Box2
 | 
			
		||||
{
 | 
			
		||||
  min: Vector2
 | 
			
		||||
  max: Vector2
 | 
			
		||||
  constructor(min = new Vector2(+Number.POSITIVE_INFINITY, +Number.POSITIVE_INFINITY), max = new Vector2(Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY))
 | 
			
		||||
  {
 | 
			
		||||
    this.min = min
 | 
			
		||||
    this.max = max
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 获取面积 */
 | 
			
		||||
  get area(): number
 | 
			
		||||
  {
 | 
			
		||||
    return (this.max.x - this.min.x) * (this.max.y - this.min.y)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**  */
 | 
			
		||||
  set(min: Vector2, max: Vector2): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.copy(min)
 | 
			
		||||
    this.max.copy(max)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setFromPoints(points: Iterable<Point>): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.makeEmpty()
 | 
			
		||||
    for (let p of points)
 | 
			
		||||
    {
 | 
			
		||||
      this.expandByPoint(p)
 | 
			
		||||
    }
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static _setFromCenterAndSize_v1 = new Vector2()
 | 
			
		||||
  setFromCenterAndSize(center: Vector2, size: Vector2): Box2
 | 
			
		||||
  {
 | 
			
		||||
    const v1 = Box2._setFromCenterAndSize_v1
 | 
			
		||||
    const halfSize = v1.copy(size).multiplyScalar(0.5)
 | 
			
		||||
    this.min.copy(center).sub(halfSize)
 | 
			
		||||
    this.max.copy(center).add(halfSize)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clone(): Box2
 | 
			
		||||
  {
 | 
			
		||||
    return new (this.constructor as any)().copy(this)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  copy(box: Box2): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.copy(box.min)
 | 
			
		||||
    this.max.copy(box.max)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  makeEmpty(): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.x = this.min.y = +Number.POSITIVE_INFINITY
 | 
			
		||||
    this.max.x = this.max.y = Number.NEGATIVE_INFINITY
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isEmpty(): boolean
 | 
			
		||||
  {
 | 
			
		||||
    // this is a more robust check for empty than (volume <= 0) because volume can get positive with two negative axes
 | 
			
		||||
    return (this.max.x < this.min.x) || (this.max.y < this.min.y)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCenter(result: Vector2 = new Vector2()): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return this.isEmpty() ? result.set(0, 0) : result.addVectors(this.min, this.max).multiplyScalar(0.5)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getSize(result: Vector2 = new Vector2()): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return this.isEmpty() ? result.set(0, 0) : result.subVectors(this.max, this.min)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  expandByPoint(point: Point): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.min(point)
 | 
			
		||||
    this.max.max(point)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  expandByVector(vector: Vector2): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.sub(vector)
 | 
			
		||||
    this.max.add(vector)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  expandByScalar(scalar: number): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.addScalar(-scalar)
 | 
			
		||||
    this.max.addScalar(scalar)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  containsPoint(point: Vector2): boolean
 | 
			
		||||
  {
 | 
			
		||||
    if (point.x < this.min.x || point.x > this.max.x
 | 
			
		||||
            || point.y < this.min.y || point.y > this.max.y)
 | 
			
		||||
    {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  containsBox(box: Box2): boolean
 | 
			
		||||
  {
 | 
			
		||||
    if ((this.min.x <= box.min.x) && (box.max.x <= this.max.x)
 | 
			
		||||
            && (this.min.y <= box.min.y) && (box.max.y <= this.max.y))
 | 
			
		||||
    {
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getParameter(point: Vector2, result: Vector2 = new Vector2()): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    // This can potentially have a divide by zero if the box
 | 
			
		||||
    // has a size dimension of 0.
 | 
			
		||||
    return result.set(
 | 
			
		||||
      (point.x - this.min.x) / (this.max.x - this.min.x),
 | 
			
		||||
      (point.y - this.min.y) / (this.max.y - this.min.y),
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  intersectsBox(box: Box2): boolean
 | 
			
		||||
  {
 | 
			
		||||
    // using 6 splitting planes to rule out intersections.
 | 
			
		||||
    if (box.max.x < this.min.x || box.min.x > this.max.x
 | 
			
		||||
            || box.max.y < this.min.y || box.min.y > this.max.y)
 | 
			
		||||
    {
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clampPoint(point: Vector2, result: Vector2 = new Vector2()): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return result.copy(point).clamp(this.min, this.max)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static _distanceToPoint_v1 = new Vector2()
 | 
			
		||||
  distanceToPoint(point: Vector2): number
 | 
			
		||||
  {
 | 
			
		||||
    const v1 = Box2._distanceToPoint_v1
 | 
			
		||||
    const clampedPoint = v1.copy(point).clamp(this.min, this.max)
 | 
			
		||||
    return clampedPoint.sub(point).length()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  intersect(box: Box2): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.max(box.min)
 | 
			
		||||
    this.max.min(box.max)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  union(box: Box2): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.min(box.min)
 | 
			
		||||
    this.max.max(box.max)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  translate(offset: Point): Box2
 | 
			
		||||
  {
 | 
			
		||||
    this.min.add(offset)
 | 
			
		||||
    this.max.add(offset)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  equals(box: Box2): boolean
 | 
			
		||||
  {
 | 
			
		||||
    return box.min.equals(this.min) && box.max.equals(this.max)
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										25
									
								
								samples/handleAbility/common/ClipperCpp.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								samples/handleAbility/common/ClipperCpp.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import * as clipperLib from 'js-angusj-clipper/web/index.js'
 | 
			
		||||
 | 
			
		||||
// nodejs style require
 | 
			
		||||
 | 
			
		||||
export const clipperCpp: { lib?: clipperLib.ClipperLibWrapper } = {}
 | 
			
		||||
export function InitClipperCpp(): Promise<void>
 | 
			
		||||
{
 | 
			
		||||
  if (clipperCpp.lib)
 | 
			
		||||
    return
 | 
			
		||||
  if (!globalThis.document)
 | 
			
		||||
    globalThis.document = {} as any
 | 
			
		||||
  return new Promise((res, rej) =>
 | 
			
		||||
  {
 | 
			
		||||
    clipperLib.loadNativeClipperLibInstanceAsync(
 | 
			
		||||
      // let it autodetect which one to use, but also available WasmOnly and AsmJsOnly
 | 
			
		||||
      // clipperLib.NativeClipperLibRequestedFormat.WasmWithAsmJsFallback
 | 
			
		||||
      clipperLib.NativeClipperLibRequestedFormat.WasmOnly,
 | 
			
		||||
    ).then((c) =>
 | 
			
		||||
    {
 | 
			
		||||
      clipperCpp.lib = c
 | 
			
		||||
      res()
 | 
			
		||||
      console.log('载入成功!')
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								samples/handleAbility/common/Filer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								samples/handleAbility/common/Filer.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
/**
 | 
			
		||||
 * CAD文件数据
 | 
			
		||||
 */
 | 
			
		||||
export class NestFiler
 | 
			
		||||
{
 | 
			
		||||
  private readIndex: number = 0
 | 
			
		||||
  constructor(public _datas: any[] = []) { }
 | 
			
		||||
 | 
			
		||||
  Clear()
 | 
			
		||||
  {
 | 
			
		||||
    this._datas.length = 0
 | 
			
		||||
    return this.Reset()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Reset()
 | 
			
		||||
  {
 | 
			
		||||
    this.readIndex = 0
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Write(data: any)
 | 
			
		||||
  {
 | 
			
		||||
    this._datas.push(data)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Read(): any
 | 
			
		||||
  {
 | 
			
		||||
    return this._datas[this.readIndex++]
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										255
									
								
								samples/handleAbility/common/LayoutEngine/Curves2Parts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								samples/handleAbility/common/LayoutEngine/Curves2Parts.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
import type { PolylineProps } from 'cadapi'
 | 
			
		||||
import { Circle, Polyline, Polyline2Points } from 'cadapi'
 | 
			
		||||
import { EndType, JoinType } from 'js-angusj-clipper/web'
 | 
			
		||||
import type { Box3, Vector3 } from 'three'
 | 
			
		||||
import { Vector2 } from 'three'
 | 
			
		||||
import { clipperCpp } from '../ClipperCpp'
 | 
			
		||||
import type { Point } from '../Point' 
 | 
			
		||||
import { Path, PathScale } from '../core/Path'
 | 
			
		||||
import type { IOffset } from './Simplify2'
 | 
			
		||||
import { SimplifyDouglasPeucker } from './Simplify2'
 | 
			
		||||
 | 
			
		||||
/** 内外接多边形 */
 | 
			
		||||
export function Circle2Points(circle: Circle, knifRadius: number, splitSize = 10, outside = false): Point[] {
 | 
			
		||||
  let radius = circle.Radius
 | 
			
		||||
  const an = Math.PI * 2 / splitSize
 | 
			
		||||
 | 
			
		||||
  if (outside)
 | 
			
		||||
    radius = radius / Math.cos(an / 2) + knifRadius
 | 
			
		||||
  else
 | 
			
		||||
    radius -= knifRadius
 | 
			
		||||
 | 
			
		||||
  const cenP = circle.Center
 | 
			
		||||
  const pts: Vector3[] = []
 | 
			
		||||
  for (let i = 0; i < splitSize; i++)
 | 
			
		||||
    pts.push(polar(cenP.clone(), an * i, radius))
 | 
			
		||||
 | 
			
		||||
  return pts as Point[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function Curve2Path(curve: Polyline, outside = false): Path {
 | 
			
		||||
  if (curve.IsClockWise)
 | 
			
		||||
    curve.Reverse()
 | 
			
		||||
  const w = new CurveWrap(curve, 3, outside)
 | 
			
		||||
  return new Path(outside ? w.GetOutsidePoints() : w.GetInsidePoints())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class CurveWrap {
 | 
			
		||||
  BoundingBox: Box3
 | 
			
		||||
 | 
			
		||||
  Area: number
 | 
			
		||||
 | 
			
		||||
  SimplyPolyline: Polyline
 | 
			
		||||
  SimplyOffset: IOffset
 | 
			
		||||
  Used = false
 | 
			
		||||
  Holes: CurveWrap[] = []
 | 
			
		||||
 | 
			
		||||
  Points: Point[]
 | 
			
		||||
 | 
			
		||||
  constructor(public Curve: Polyline | Circle, public KnifRadius: number, public IsOutside: boolean) {
 | 
			
		||||
    this.BoundingBox = Curve.BoundingBox
 | 
			
		||||
 | 
			
		||||
    if (Curve instanceof Polyline) {
 | 
			
		||||
      const pts = Polyline2Points(Curve, IsOutside, 0)[1]
 | 
			
		||||
      /**
 | 
			
		||||
       * 精简算法SimplifyDouglasPeucker 会导致轮廓变大,
 | 
			
		||||
       *  修改成直接取点   陈雄 QQ聊天记录 23.9.18
 | 
			
		||||
       * 23.10.9  by lrx
 | 
			
		||||
       */
 | 
			
		||||
 | 
			
		||||
      const [spts, offset] = SimplifyDouglasPeucker(pts, KnifRadius ** 2 + KnifRadius)
 | 
			
		||||
      if (spts.length !== pts.length && spts.length > 2) {
 | 
			
		||||
        this.SimplyOffset = offset
 | 
			
		||||
 | 
			
		||||
        this.SimplyPolyline = Path2Polyline(spts)
 | 
			
		||||
        this.Curve = this.SimplyPolyline// 保险起见,也更新它
 | 
			
		||||
        this.Area = this.SimplyPolyline.Area
 | 
			
		||||
      }
 | 
			
		||||
      else// 此处更新多段线
 | 
			
		||||
      { this.Curve = Path2Polyline(pts) }
 | 
			
		||||
      this.Points = spts
 | 
			
		||||
 | 
			
		||||
      // 以下修改后的
 | 
			
		||||
      // this.Curve = Path2Polyline(pts);
 | 
			
		||||
      // this.Points = pts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.Area === undefined)
 | 
			
		||||
      this.Area = this.Curve.Area
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ContainsCurve(curve: CurveWrap): boolean {
 | 
			
		||||
    if (this.SimplyPolyline)
 | 
			
		||||
      return this.SimplyPolyline.PtInCurve(curve.Curve.StartPoint)
 | 
			
		||||
    return this.Curve.PtInCurve(curve.Curve.StartPoint)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetOutsidePoints(): Point[] {
 | 
			
		||||
    if (this.Curve instanceof Circle) {
 | 
			
		||||
      const pts = Circle2Points(this.Curve, this.KnifRadius, 10, true)
 | 
			
		||||
      return pts
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      const pl = this.SimplyPolyline || this.Curve
 | 
			
		||||
      let offset = this.KnifRadius
 | 
			
		||||
      if (this.SimplyOffset)
 | 
			
		||||
        offset += this.SimplyOffset.positiveOffset
 | 
			
		||||
 | 
			
		||||
      if (offset > 0) {
 | 
			
		||||
        let pts = pl.GetStretchPoints() as Point[]
 | 
			
		||||
        pts = clipperCpp.lib.offsetToPaths({
 | 
			
		||||
          delta: offset * 1e4,
 | 
			
		||||
          offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }],
 | 
			
		||||
        })[0]
 | 
			
		||||
        try {
 | 
			
		||||
          PathScale(pts, 1e-4)
 | 
			
		||||
        }
 | 
			
		||||
        catch {
 | 
			
		||||
          console.log('err')
 | 
			
		||||
        }
 | 
			
		||||
        return pts
 | 
			
		||||
      }
 | 
			
		||||
      else { return this.Points }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetInsidePoints(): Point[] {
 | 
			
		||||
    return this.GetInsidePoints2(this.KnifRadius)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetInsidePoints2(d: number): Point[] {
 | 
			
		||||
    if (this.Curve instanceof Circle) {
 | 
			
		||||
      const pts = Circle2Points(this.Curve, d, 10, false)
 | 
			
		||||
      return pts
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      const pl = this.SimplyPolyline || this.Curve
 | 
			
		||||
      let offset = -d
 | 
			
		||||
      if (this.SimplyOffset)
 | 
			
		||||
        offset += this.SimplyOffset.negativeOffset
 | 
			
		||||
 | 
			
		||||
      if (offset < -0.01) {
 | 
			
		||||
        const pls = pl.GetOffsetCurves(offset)
 | 
			
		||||
        if (pls.length)
 | 
			
		||||
          return pls[0].GetStretchPoints()
 | 
			
		||||
      }
 | 
			
		||||
      else { return this.Points }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 引入Polyline 已经含刀半径, 获得精简后的点阵 */
 | 
			
		||||
  GetOrgPoints(outside = true): Point[] {
 | 
			
		||||
    if (this.Curve instanceof Circle) {
 | 
			
		||||
      const pts = Circle2Points(this.Curve, 0, 10, outside)
 | 
			
		||||
      return pts
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      const pl = this.SimplyPolyline || this.Curve
 | 
			
		||||
      let offset = 0
 | 
			
		||||
      if (this.SimplyOffset)
 | 
			
		||||
        offset += this.SimplyOffset.positiveOffset
 | 
			
		||||
 | 
			
		||||
      if (offset > 0) {
 | 
			
		||||
        let pts = pl.GetStretchPoints() as Point[]
 | 
			
		||||
        pts = clipperCpp.lib.offsetToPaths({
 | 
			
		||||
          delta: offset * 1e4,
 | 
			
		||||
          offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }],
 | 
			
		||||
        })[0]
 | 
			
		||||
        try {
 | 
			
		||||
          PathScale(pts, 1e-4)
 | 
			
		||||
        }
 | 
			
		||||
        catch {
 | 
			
		||||
          console.log('err')
 | 
			
		||||
        }
 | 
			
		||||
        return pts
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        return this.Points
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 多段线 转点整  已弃用,整合到CAD api 23.11.2 */
 | 
			
		||||
// export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: number): [Polyline, Point[]]
 | 
			
		||||
// {
 | 
			
		||||
//     let pts: Point[] = [];
 | 
			
		||||
 | 
			
		||||
//     if (!outside) knifRadius = -knifRadius;
 | 
			
		||||
//     if (pl.IsClockWise) pl.Reverse();
 | 
			
		||||
//     for (let i = 0; i < pl.EndParam; i++)
 | 
			
		||||
//     {
 | 
			
		||||
//         pts.push(pl.GetPointAtParam(i));
 | 
			
		||||
 | 
			
		||||
//         let bul = pl.GetBulgeAt(i);
 | 
			
		||||
//         if (bul !== 0)
 | 
			
		||||
//         {
 | 
			
		||||
//             let arc = pl.GetCurveAtIndex(i) as Arc;
 | 
			
		||||
 | 
			
		||||
//             let allAngle = arc.AllAngle;
 | 
			
		||||
//             let arcLength = arc.Length;
 | 
			
		||||
 | 
			
		||||
//             // let splitCount = Math.round(allAngle / 0.4);
 | 
			
		||||
//             // if (arcLength < 300)
 | 
			
		||||
//             //     splitCount = 2;
 | 
			
		||||
//             // else
 | 
			
		||||
//             //     splitCount = Math.max(arcLength / 200, splitCount,2);
 | 
			
		||||
 | 
			
		||||
//             let minCount = Math.floor(allAngle * 4 / Math.PI);
 | 
			
		||||
//             let splitCount = Math.round(allAngle / 0.4);
 | 
			
		||||
//             if (arcLength < 300)
 | 
			
		||||
//                 splitCount = Math.max(2, minCount);
 | 
			
		||||
//             else
 | 
			
		||||
//                 splitCount = Math.max(Math.floor(arcLength / 200), splitCount,2, minCount);
 | 
			
		||||
 | 
			
		||||
//             let radius = arc.Radius;
 | 
			
		||||
//             if (outside === bul > 0)
 | 
			
		||||
//                 radius = radius / Math.cos(allAngle / (splitCount * 2));
 | 
			
		||||
 | 
			
		||||
//             let cp = arc.Center;
 | 
			
		||||
//             for (let j = outside ? 0.5 : 0; j < splitCount; j++)
 | 
			
		||||
//             {
 | 
			
		||||
//                 let a = arc.GetAngleAtParam(j * (1 / splitCount));
 | 
			
		||||
//                 let p = polar(cp.clone(), a, radius);
 | 
			
		||||
//                 pts.push(p);
 | 
			
		||||
//             }
 | 
			
		||||
//         }
 | 
			
		||||
//     }
 | 
			
		||||
 | 
			
		||||
//     if (knifRadius !== 0)
 | 
			
		||||
//     {
 | 
			
		||||
//         pts = clipperCpp.lib.offsetToPaths({
 | 
			
		||||
//             delta: knifRadius * 1e4,
 | 
			
		||||
//             offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }]
 | 
			
		||||
//         })[0];
 | 
			
		||||
//         PathScale(pts, 1e-4);
 | 
			
		||||
//     }
 | 
			
		||||
//     return [pl, pts];
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
export function Path2Polyline(path: Point[]): Polyline {
 | 
			
		||||
  const pl = new Polyline()
 | 
			
		||||
  pl.LineData = path.map((p) => {
 | 
			
		||||
    return { pt: new Vector2(p.x, p.y), bul: 0 }
 | 
			
		||||
  })
 | 
			
		||||
  pl.CloseMark = true
 | 
			
		||||
  return pl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function Points2Polyline(pts: any[]): Polyline {
 | 
			
		||||
  const lined: PolylineProps[] = []
 | 
			
		||||
  const count = pts.length
 | 
			
		||||
  for (let i = 0; i < count; i++) {
 | 
			
		||||
    const p0 = pts[i]
 | 
			
		||||
    lined.push({ pt: new Vector2(p0.x, p0.y), bul: p0.bul })
 | 
			
		||||
  }
 | 
			
		||||
  const pls = new Polyline(lined)
 | 
			
		||||
  pls.CloseMark = true
 | 
			
		||||
  return pls
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function polar<T extends Vector2 | Vector3>(v: T, an: number, dis: number): T {
 | 
			
		||||
  v.x += Math.cos(an) * dis
 | 
			
		||||
  v.y += Math.sin(an) * dis
 | 
			
		||||
  return v
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										369
									
								
								samples/handleAbility/common/LayoutEngine/PolylineHelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								samples/handleAbility/common/LayoutEngine/PolylineHelper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,369 @@
 | 
			
		||||
import type { PolylineProps } from 'cadapi'
 | 
			
		||||
import { CADFiler, Circle, Polyline, Status, VKnifToolPath, isTargetCurInOrOnSourceCur } from 'cadapi'
 | 
			
		||||
import type { Box3 } from 'three'
 | 
			
		||||
import { Vector2, Vector3 } from 'three'
 | 
			
		||||
import { arrayRemoveDuplicateBySort } from '../ArrayExt'  
 | 
			
		||||
import type { Curve2d } from '../base/CAD'
 | 
			
		||||
import { Arc2d, Point2d, copyTextToClipboard } from '../base/CAD'
 | 
			
		||||
import { CurveWrap } from './Curves2Parts'
 | 
			
		||||
 | 
			
		||||
// import type { Curve2d } from '../../common/base/CAD'
 | 
			
		||||
 | 
			
		||||
export class PolylineHelper {
 | 
			
		||||
  /** 创建闭合多段线 */
 | 
			
		||||
  static create(pts: any[], closeMark = false): Polyline {
 | 
			
		||||
    let lined: PolylineProps[] = []
 | 
			
		||||
    let count = pts.length
 | 
			
		||||
    for (let i = 0; i < count; i++) {
 | 
			
		||||
      let p0 = pts[i]
 | 
			
		||||
 | 
			
		||||
      lined.push({ pt: new Vector2(p0.x, p0.y), bul: p0.bul || 0 })
 | 
			
		||||
    }
 | 
			
		||||
    let pls = new Polyline(lined)
 | 
			
		||||
    pls.CloseMark = closeMark
 | 
			
		||||
    return pls
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static createByCurve2d(curs: Curve2d[], closeMark = true): Polyline {
 | 
			
		||||
    let lined: PolylineProps[] = []
 | 
			
		||||
    for (let cur of curs) {
 | 
			
		||||
      let x = cur.StartPoint.m_X
 | 
			
		||||
      let y = cur.StartPoint.m_Y
 | 
			
		||||
      let bul = 0
 | 
			
		||||
      if (cur instanceof Arc2d)
 | 
			
		||||
        bul = cur.Bul || 0
 | 
			
		||||
      lined.push({ pt: new Vector2(x, y), bul })
 | 
			
		||||
    }
 | 
			
		||||
    let pls = new Polyline(lined)
 | 
			
		||||
    pls.CloseMark = true
 | 
			
		||||
    return pls
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static createByPts(pts: any[], buls: number[], closeMark = false): Polyline {
 | 
			
		||||
    let plps: PolylineProps[] = []
 | 
			
		||||
    let count = pts.length
 | 
			
		||||
    for (let i = 0; i < count; i++) {
 | 
			
		||||
      let p0 = pts[i]
 | 
			
		||||
      plps.push({ pt: new Vector2(p0.x, p0.y), bul: buls[i] })
 | 
			
		||||
    }
 | 
			
		||||
    let pls = new Polyline(plps)
 | 
			
		||||
    pls.CloseMark = closeMark
 | 
			
		||||
    return pls
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getSimplePoints(pts: any[], offset: number): any[] {
 | 
			
		||||
    let pl = PolylineHelper.create(pts)
 | 
			
		||||
    pl.CloseMark = true
 | 
			
		||||
    let cureW = new CurveWrap(pl, offset, true)
 | 
			
		||||
    let pts2 = cureW.GetOutsidePoints()
 | 
			
		||||
    arrayRemoveDuplicateBySort(pts2, (p1, p2) => (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) < 1e-2)
 | 
			
		||||
    return pts2
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static createByWidthLength(w: number, l: number): Polyline {
 | 
			
		||||
    let plps: PolylineProps[] = []
 | 
			
		||||
    plps.push({ pt: new Vector2(0, 0), bul: 0 })
 | 
			
		||||
    plps.push({ pt: new Vector2(w, 0), bul: 0 })
 | 
			
		||||
    plps.push({ pt: new Vector2(w, l), bul: 0 })
 | 
			
		||||
    plps.push({ pt: new Vector2(0, l), bul: 0 })
 | 
			
		||||
    let pls = new Polyline(plps)
 | 
			
		||||
    pls.CloseMark = true
 | 
			
		||||
    return pls
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 多段线,添加位置移动 返回新多段线 */
 | 
			
		||||
  static moveTo(pl: Polyline, x: number, y: number): Polyline {
 | 
			
		||||
    let lindData = pl.LineData
 | 
			
		||||
    let pos = pl.Position
 | 
			
		||||
 | 
			
		||||
    let newPts: PolylineProps[] = []
 | 
			
		||||
    for (let p of lindData) {
 | 
			
		||||
      let nx = p.pt.x + pos.x + x
 | 
			
		||||
      let ny = p.pt.y + pos.y + y
 | 
			
		||||
      if (ny < 7.9) {
 | 
			
		||||
        // console.log('修边小于 7.9????', ny)
 | 
			
		||||
      }
 | 
			
		||||
      let bul = p.bul
 | 
			
		||||
      newPts.push({ pt: new Vector2(nx, ny), bul })
 | 
			
		||||
    }
 | 
			
		||||
    let npl = new Polyline(newPts)
 | 
			
		||||
    npl.CloseMark = pl.CloseMark
 | 
			
		||||
    return npl
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 重设 多段线的几点 */
 | 
			
		||||
  static resetPosition(pl: Polyline): Polyline {
 | 
			
		||||
    let lindData = pl.LineData
 | 
			
		||||
    let pos = pl.Position
 | 
			
		||||
 | 
			
		||||
    let newPts: PolylineProps[] = []
 | 
			
		||||
    for (let p of lindData) {
 | 
			
		||||
      let nx = p.pt.x + pos.x
 | 
			
		||||
      let ny = p.pt.y + pos.y
 | 
			
		||||
      let bul = p.bul
 | 
			
		||||
      newPts.push({ pt: new Vector2(nx, ny), bul })
 | 
			
		||||
    }
 | 
			
		||||
    let npl = new Polyline(newPts)
 | 
			
		||||
    npl.CloseMark = pl.CloseMark
 | 
			
		||||
    return npl
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 获得v型刀走刀路径 */o
 | 
			
		||||
  static getVModelPoints(pl: Polyline, depth: number, ang: number): any[] {
 | 
			
		||||
    // let ang = Math.PI * (0.5 * angle) / 180  ;
 | 
			
		||||
    let ps = []
 | 
			
		||||
    let bx = pl.Position.x
 | 
			
		||||
    let by = pl.Position.y
 | 
			
		||||
    if (ang > 0.01) {
 | 
			
		||||
      let rt = VKnifToolPath(pl, depth, ang / 2)
 | 
			
		||||
      ps = rt.map((t) => { return { x: t.pt.x + bx, y: t.pt.y + by, z: t.pt.z, bul: t.bul, r: 0 } })
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      ps = pl.LineData.map((t) => { return { x: t.pt.x + bx, y: t.pt.y + by, z: 0, bul: t.bul, r: 0 } })
 | 
			
		||||
    }
 | 
			
		||||
    for (let i = 0; i < ps.length; i++) {
 | 
			
		||||
      let p = ps[i]
 | 
			
		||||
      if (p.bul == 0)
 | 
			
		||||
        continue
 | 
			
		||||
      let p2 = (i == ps.length - 1 ? ps[0] : ps[i + 1])
 | 
			
		||||
      let r = this.getArcRadius(p.x, p.y, p2.x, p2.y, p.bul)
 | 
			
		||||
      p.r = r
 | 
			
		||||
    }
 | 
			
		||||
    return ps
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static ConverPolyLin2Circle(polyline: Polyline, fuzz = 0.1): Circle | undefined {
 | 
			
		||||
    let box = polyline.BoundingBox
 | 
			
		||||
    let size = box.getSize(new Vector3())
 | 
			
		||||
    if (!this.equaln(size.x, size.y, fuzz))// 盒子四方
 | 
			
		||||
      return undefined
 | 
			
		||||
 | 
			
		||||
    let circleLength = 2 * Math.PI * size.x
 | 
			
		||||
    if (!this.equaln(circleLength, polyline.Length, fuzz * 2))
 | 
			
		||||
      return undefined
 | 
			
		||||
 | 
			
		||||
    let circleArea = Math.PI * size.x * size.x
 | 
			
		||||
    if (!this.equaln(circleArea, polyline.Area, fuzz * 2))
 | 
			
		||||
      return undefined
 | 
			
		||||
 | 
			
		||||
    let r = size.x// 必须备份(因为我们要复用这个vector变量)
 | 
			
		||||
    return new Circle(box.getCenter(size), r)
 | 
			
		||||
  }
 | 
			
		||||
  // 有问题 
 | 
			
		||||
  static getVModelPoints_offset(pl: Polyline, offset: number, depth: number, angle: number) {
 | 
			
		||||
    let npl = offset == 0 ? pl : pl.GetOffsetCurves(offset)[0]
 | 
			
		||||
    // if(offset != 0)
 | 
			
		||||
    // {
 | 
			
		||||
    //     ClipboardTest.write2PolyLine(pl,npl);
 | 
			
		||||
    // }
 | 
			
		||||
    return PolylineHelper.getVModelPoints(npl, depth, angle)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getArcRadius(x1: number, y1: number, x2: number, y2: number, bul: number): number {
 | 
			
		||||
    let bul2 = Math.abs(bul)
 | 
			
		||||
    let d = Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) / 2
 | 
			
		||||
    return 0.5 * d * (1 + bul2 ** 2) / bul2
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 圆 转 多段线
 | 
			
		||||
  static cicleToPolyline(c: Circle): Polyline {
 | 
			
		||||
    let arcs = c.GetSplitCurves([0, 0.5])
 | 
			
		||||
    let pl = Polyline.FastCombine(arcs)
 | 
			
		||||
    return pl
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 判断多段线是否 重叠 */
 | 
			
		||||
  static isIntersect(pl1: Polyline, pl2: Polyline): boolean {
 | 
			
		||||
    let box1 = this.getBox(pl1)
 | 
			
		||||
    let box2 = this.getBox(pl2)
 | 
			
		||||
 | 
			
		||||
    if (!box1.intersectsBox(box2))
 | 
			
		||||
      return false // 肯定不相交
 | 
			
		||||
 | 
			
		||||
    let ipts = pl1.IntersectWith(pl2, 0)
 | 
			
		||||
    if (ipts.length === 0) {
 | 
			
		||||
      if (pl1.Area > pl2.Area)// 缓存面积
 | 
			
		||||
      {
 | 
			
		||||
        if (isTargetCurInOrOnSourceCur(pl1, pl2))
 | 
			
		||||
          return true // 包含
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        if (isTargetCurInOrOnSourceCur(pl2, pl1))
 | 
			
		||||
          return true // 包含
 | 
			
		||||
      }
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return true // 有交点  一定有交集
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 多段线 圆弧合并
 | 
			
		||||
  static mergeCurve(pl2: Polyline): Polyline {
 | 
			
		||||
    const curves = pl2.Explode()
 | 
			
		||||
    arrayRemoveDuplicateBySort(curves, (c1, c2) => {
 | 
			
		||||
      return c1.Join(c2) === Status.True
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    return Polyline.FastCombine(curves)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * pl2 包含在pl1 内
 | 
			
		||||
   *
 | 
			
		||||
   */
 | 
			
		||||
  // static isInside(pl1:Polyline,pl2:Polyline):boolean
 | 
			
		||||
  // {
 | 
			
		||||
  //     let box1 = this.getBox(pl1);
 | 
			
		||||
  //     let box2 = this.getBox(pl2);
 | 
			
		||||
  //     if (!box1.intersectsBox(box2)) return false; //肯定不相交
 | 
			
		||||
 | 
			
		||||
  //     let ipts = pl1.IntersectWith(pl2, 0);
 | 
			
		||||
  //     if (ipts.length > 0) return true; //有交点  一定有交集
 | 
			
		||||
  // }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 两片板的干涉检查
 | 
			
		||||
   *
 | 
			
		||||
   * @param pl1
 | 
			
		||||
   * @param pls1_inner
 | 
			
		||||
   * @param pls1_model
 | 
			
		||||
   * @param pl2
 | 
			
		||||
   * @param pls2_inner
 | 
			
		||||
   * @param pls2_model
 | 
			
		||||
   * @returns
 | 
			
		||||
   */
 | 
			
		||||
  static isOverLap(pl1: Polyline, pls1_inner: Polyline[], pls1_model: Polyline[], pl2: Polyline, pls2_inner: Polyline[], pls2_model: Polyline[]) {
 | 
			
		||||
    // 是否干涉, 被包含在造型洞,不算干涉
 | 
			
		||||
    let isOverlap = this.boxIsOverlap(pl1, pls1_inner, pl2, pls2_inner)
 | 
			
		||||
 | 
			
		||||
    if (isOverlap)
 | 
			
		||||
      return true
 | 
			
		||||
 | 
			
		||||
    // 造型 ,2v 刀路 是否干涉
 | 
			
		||||
    for (let pl1_model of pls1_model) {
 | 
			
		||||
      if (pl1_model.IntersectWith(pl2, 0).length > 0)
 | 
			
		||||
        return true
 | 
			
		||||
      for (let pl2_inner of pls2_inner) {
 | 
			
		||||
        if (pl1_model.IntersectWith(pl2_inner, 0).length > 0)
 | 
			
		||||
          return true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (let pl2_model of pls2_model) {
 | 
			
		||||
      if (pl2_model.IntersectWith(pl1, 0).length > 0)
 | 
			
		||||
        return true
 | 
			
		||||
      for (let pl1_inner of pls1_inner) {
 | 
			
		||||
        if (pl2_model.IntersectWith(pl1_inner, 0).length > 0)
 | 
			
		||||
          return true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static boxIsOverlap(pl1: Polyline, pls1_inner: Polyline[], pl2: Polyline, pls2_inner: Polyline[]) {
 | 
			
		||||
    let box1 = this.getBox(pl1)
 | 
			
		||||
    let box2 = this.getBox(pl2)
 | 
			
		||||
 | 
			
		||||
    if (!box1.intersectsBox(box2))
 | 
			
		||||
      return false // 肯定不相交
 | 
			
		||||
 | 
			
		||||
    let ipts = pl1.IntersectWith(pl2, 0)
 | 
			
		||||
    if (ipts.length > 0)
 | 
			
		||||
      return true // 有交点  一定有交集
 | 
			
		||||
 | 
			
		||||
    if (pl1.Area > pl2.Area)// 缓存面积
 | 
			
		||||
    {
 | 
			
		||||
      if (isTargetCurInOrOnSourceCur(pl1, pl2)) // pl1包含 pl2
 | 
			
		||||
      {
 | 
			
		||||
        for (let mpl of pls1_inner) // 如果pl1有造型洞包含pl2, 则表示不干涉,返回false
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
          if (isTargetCurInOrOnSourceCur(mpl, pl2) == true)
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      if (isTargetCurInOrOnSourceCur(pl2, pl1)) // pl2包含 pl1
 | 
			
		||||
      {
 | 
			
		||||
        for (let mpl of pls2_inner) // 如果pl2有造型洞包含pl1, 则表示不干涉,返回false
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
          if (isTargetCurInOrOnSourceCur(mpl, pl1) == true)
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 判断 点是否在多段线内 */
 | 
			
		||||
  static isPointInPolyline(pl1: Polyline, x: number, y: number): boolean {
 | 
			
		||||
    return pl1.PtInCurve(new Vector3(x, y, 0))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getBox(pl1: Polyline): Box3 {
 | 
			
		||||
    if (!pl1.box_tp)
 | 
			
		||||
      pl1.box_tp = pl1.BoundingBox
 | 
			
		||||
 | 
			
		||||
    return pl1.box_tp as Box3
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getArea(pl1: Polyline): number {
 | 
			
		||||
    if (!pl1.area_tp)
 | 
			
		||||
      pl1.area_tp = pl1.Area
 | 
			
		||||
 | 
			
		||||
    return pl1.area_tp as number
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getPath(pl: Polyline): Path2D {
 | 
			
		||||
    let path = new Path2D()
 | 
			
		||||
    let p0 = pl.LineData[0].pt
 | 
			
		||||
    path.moveTo(p0.x, p0.y)
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < pl.LineData.length; i++) {
 | 
			
		||||
      let p0 = pl.LineData[i].pt
 | 
			
		||||
      let p1 = (i == pl.LineData.length - 1) ? pl.LineData[0].pt : pl.LineData[i + 1].pt
 | 
			
		||||
      let bul = pl.LineData[i].bul
 | 
			
		||||
      if (bul == 0) {
 | 
			
		||||
        path.lineTo(p1.x, p1.y)
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        let arc = new Arc2d(new Point2d(p0.x, p0.y), new Point2d(p1.x, p1.y), bul)
 | 
			
		||||
        path.arc(arc.m_Center.m_X, arc.m_Center.m_Y, arc.m_Radius, arc.m_StartAngle, arc.m_EndAngle, bul < 0)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    path.closePath()
 | 
			
		||||
    return path
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static equaln(v1: number, v2: number, fuzz = 1e-5) {
 | 
			
		||||
    return Math.abs(v1 - v2) <= fuzz
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static toClipboard(en: Polyline | any) {
 | 
			
		||||
    let f = new CADFiler()
 | 
			
		||||
    f.Write(1)// 实体个数
 | 
			
		||||
    f.WriteObject(en)
 | 
			
		||||
 | 
			
		||||
    copyTextToClipboard(f.ToString())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getStrPLs(ens: any[]) {
 | 
			
		||||
    if (ens.length == 0)
 | 
			
		||||
      return ''
 | 
			
		||||
    let f = new CADFiler()
 | 
			
		||||
    f.Write(ens.length)// 实体个数
 | 
			
		||||
    for (let en of ens)
 | 
			
		||||
      f.WriteObject(en)
 | 
			
		||||
 | 
			
		||||
    return f.ToString()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								samples/handleAbility/common/LayoutEngine/Simplify2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								samples/handleAbility/common/LayoutEngine/Simplify2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
import { Vector2 } from "../Vector2"  
 | 
			
		||||
 | 
			
		||||
interface P {
 | 
			
		||||
  x: number
 | 
			
		||||
  y: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IOffset {
 | 
			
		||||
  negativeOffset: number
 | 
			
		||||
  positiveOffset: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 点p到线段P1P2 的最短距离的平方,线段不延伸 */
 | 
			
		||||
function GetSqSegDist(p: P, p1: P, p2: P): number {
 | 
			
		||||
  let x = p1.x
 | 
			
		||||
  let y = p1.y
 | 
			
		||||
  let dx = p2.x - x
 | 
			
		||||
  let dy = p2.y - y
 | 
			
		||||
 | 
			
		||||
  if (dx !== 0 || dy !== 0)// 不是0长度线
 | 
			
		||||
  {
 | 
			
		||||
    const t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy)
 | 
			
		||||
    if (t > 1) {
 | 
			
		||||
      x = p2.x
 | 
			
		||||
      y = p2.y
 | 
			
		||||
    }
 | 
			
		||||
    else if (t > 0) {
 | 
			
		||||
      x += dx * t
 | 
			
		||||
      y += dy * t
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  dx = p.x - x
 | 
			
		||||
  dy = p.y - y
 | 
			
		||||
  return dx * dx + dy * dy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function CrossVector2(a: P, b: P)
 | 
			
		||||
{
 | 
			
		||||
  return a.x * b.y - a.y * b.x
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ramer-Douglas-Peucker algorithm
 | 
			
		||||
function SimplifyDPStep(points: P[], first: number, last: number, sqTolerance: number, simplified: P[], offset: IOffset): void {
 | 
			
		||||
  let maxSqDist = 0
 | 
			
		||||
  let index: number
 | 
			
		||||
  const fp = points[first]
 | 
			
		||||
  const lp = points[last]
 | 
			
		||||
 | 
			
		||||
  for (let i = first + 1; i < last; i++) {
 | 
			
		||||
    const p = points[i]
 | 
			
		||||
    const sqDist = GetSqSegDist(p, fp, lp)
 | 
			
		||||
    if (sqDist > maxSqDist) {
 | 
			
		||||
      index = i
 | 
			
		||||
      maxSqDist = sqDist
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (maxSqDist > sqTolerance) {
 | 
			
		||||
    if (index - first > 1)
 | 
			
		||||
      SimplifyDPStep(points, first, index, sqTolerance, simplified, offset)
 | 
			
		||||
    simplified.push(points[index])
 | 
			
		||||
    if (last - index > 1)
 | 
			
		||||
      SimplifyDPStep(points, index, last, sqTolerance, simplified, offset)
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    // 记录偏移
 | 
			
		||||
    const v = new Vector2(lp.x - fp.x, lp.y - fp.y).normalize()
 | 
			
		||||
    for (let i = first + 1; i < last; i++) {
 | 
			
		||||
      const p = points[i]
 | 
			
		||||
      const offsetDist = -CrossVector2(v, { x: p.x - fp.x, y: p.y - fp.y })
 | 
			
		||||
      offset.positiveOffset = Math.max(offset.positiveOffset, offsetDist)
 | 
			
		||||
      offset.negativeOffset = Math.min(offset.negativeOffset, offsetDist)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Ramer-Douglas-Peucker 算法
 | 
			
		||||
export function SimplifyDouglasPeucker(points: P[], sqTolerance: number): [P[], IOffset] {
 | 
			
		||||
  const last = points.length - 1
 | 
			
		||||
  const simplified: P[] = [points[0]]
 | 
			
		||||
  const offset: IOffset = { negativeOffset: 0, positiveOffset: 0 }
 | 
			
		||||
  SimplifyDPStep(points, 0, last, sqTolerance, simplified, offset)
 | 
			
		||||
  simplified.push(points[last])
 | 
			
		||||
  return [simplified, offset]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								samples/handleAbility/common/Point.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								samples/handleAbility/common/Point.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
export interface Point
 | 
			
		||||
{
 | 
			
		||||
  x: number
 | 
			
		||||
  y: number
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								samples/handleAbility/common/Util.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								samples/handleAbility/common/Util.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
/** 判断两个值是否相等 */
 | 
			
		||||
export function equaln(v1: number, v2: number, fuzz = 1e-5)
 | 
			
		||||
{
 | 
			
		||||
  return Math.abs(v1 - v2) <= fuzz
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 修正数组索引号 */
 | 
			
		||||
export function FixIndex(index: number, arr: Array<any> | number)
 | 
			
		||||
{
 | 
			
		||||
  let count = (Array.isArray(arr)) ? arr.length : arr
 | 
			
		||||
  if (index < 0)
 | 
			
		||||
    return count + index
 | 
			
		||||
  else if (index >= count)
 | 
			
		||||
    return index - count
 | 
			
		||||
  else
 | 
			
		||||
    return index
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取数组最大元素的索引号
 | 
			
		||||
 * @param compart t2大于t1返回t2
 | 
			
		||||
 * @returns 索引
 | 
			
		||||
 */
 | 
			
		||||
export function Max<T>(arr: T[], compart: (t1: T, t2: T) => boolean): number
 | 
			
		||||
{
 | 
			
		||||
  let best: T = arr[0]
 | 
			
		||||
  let bestIndex = 0
 | 
			
		||||
  for (let i = 1; i < arr.length; i++)
 | 
			
		||||
  {
 | 
			
		||||
    let t1 = arr[i]
 | 
			
		||||
    if (compart(best, t1))
 | 
			
		||||
    {
 | 
			
		||||
      best = t1
 | 
			
		||||
      bestIndex = i
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return bestIndex
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										335
									
								
								samples/handleAbility/common/Vector2.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								samples/handleAbility/common/Vector2.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,335 @@
 | 
			
		||||
import type { Point } from './Point.js'
 | 
			
		||||
 | 
			
		||||
/** 二维向量 */
 | 
			
		||||
export class Vector2
 | 
			
		||||
{
 | 
			
		||||
  x: number
 | 
			
		||||
  y: number
 | 
			
		||||
  readonly isVector2: boolean = true
 | 
			
		||||
  constructor(x: number = 0, y: number = 0)
 | 
			
		||||
  {
 | 
			
		||||
    this.x = x
 | 
			
		||||
    this.y = y
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  get width(): number { return this.x }
 | 
			
		||||
  set width(value: number) { this.x = value }
 | 
			
		||||
  get height(): number { return this.y }
 | 
			
		||||
  set height(value: number) { this.y = value }
 | 
			
		||||
  set(x: number, y: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = x
 | 
			
		||||
    this.y = y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setScalar(scalar: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = scalar
 | 
			
		||||
    this.y = scalar
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setX(x: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = x
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setY(y: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.y = y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setComponent(index: number, value: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    switch (index)
 | 
			
		||||
    {
 | 
			
		||||
      case 0: this.x = value; break
 | 
			
		||||
      case 1: this.y = value; break
 | 
			
		||||
      default: throw new Error(`index is out of range: ${index}`)
 | 
			
		||||
    }
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getComponent(index: number): number
 | 
			
		||||
  {
 | 
			
		||||
    switch (index)
 | 
			
		||||
    {
 | 
			
		||||
      case 0: return this.x
 | 
			
		||||
      case 1: return this.y
 | 
			
		||||
      default: throw new Error(`index is out of range: ${index}`)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clone(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return new (this.constructor as any)().copy(this)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  copy(v: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = v.x
 | 
			
		||||
    this.y = v.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  add(v: Point): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x += v.x
 | 
			
		||||
    this.y += v.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addScalar(s: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x += s
 | 
			
		||||
    this.y += s
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addVectors(a: Vector2, b: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = a.x + b.x
 | 
			
		||||
    this.y = a.y + b.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addScaledVector(v: Vector2, s: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x += v.x * s
 | 
			
		||||
    this.y += v.y * s
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sub(v: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x -= v.x
 | 
			
		||||
    this.y -= v.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  subScalar(s: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x -= s
 | 
			
		||||
    this.y -= s
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  subVectors(a: Vector2, b: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = a.x - b.x
 | 
			
		||||
    this.y = a.y - b.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  multiply(v: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x *= v.x
 | 
			
		||||
    this.y *= v.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  multiplyScalar(scalar: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    if (Number.isFinite(scalar))
 | 
			
		||||
    {
 | 
			
		||||
      this.x *= scalar
 | 
			
		||||
      this.y *= scalar
 | 
			
		||||
    } else
 | 
			
		||||
    {
 | 
			
		||||
      this.x = 0
 | 
			
		||||
      this.y = 0
 | 
			
		||||
    }
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  divide(v: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x /= v.x
 | 
			
		||||
    this.y /= v.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  divideScalar(scalar: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return this.multiplyScalar(1 / scalar)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  min(v: Point): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = Math.min(this.x, v.x)
 | 
			
		||||
    this.y = Math.min(this.y, v.y)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  max(v: Point): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = Math.max(this.x, v.x)
 | 
			
		||||
    this.y = Math.max(this.y, v.y)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clamp(min: Vector2, max: Vector2): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    // This function assumes min < max, if this assumption isn't true it will not operate correctly
 | 
			
		||||
    this.x = Math.max(min.x, Math.min(max.x, this.x))
 | 
			
		||||
    this.y = Math.max(min.y, Math.min(max.y, this.y))
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static clampScalar_min = new Vector2()
 | 
			
		||||
  private static clampScalar_max = new Vector2()
 | 
			
		||||
  clampScalar(minVal: number, maxVal: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    const min: Vector2 = Vector2.clampScalar_min.set(minVal, minVal)
 | 
			
		||||
    const max: Vector2 = Vector2.clampScalar_max.set(maxVal, maxVal)
 | 
			
		||||
    return this.clamp(min, max)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clampLength(min: number, max: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    const length: number = this.length()
 | 
			
		||||
    return this.multiplyScalar(Math.max(min, Math.min(max, length)) / length)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  floor(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = Math.floor(this.x)
 | 
			
		||||
    this.y = Math.floor(this.y)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ceil(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = Math.ceil(this.x)
 | 
			
		||||
    this.y = Math.ceil(this.y)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  round(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = Math.round(this.x)
 | 
			
		||||
    this.y = Math.round(this.y)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  roundToZero(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = (this.x < 0) ? Math.ceil(this.x) : Math.floor(this.x)
 | 
			
		||||
    this.y = (this.y < 0) ? Math.ceil(this.y) : Math.floor(this.y)
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  negate(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = -this.x
 | 
			
		||||
    this.y = -this.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  dot(v: Vector2): number
 | 
			
		||||
  {
 | 
			
		||||
    return this.x * v.x + this.y * v.y
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lengthSq(): number
 | 
			
		||||
  {
 | 
			
		||||
    return this.x * this.x + this.y * this.y
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  length(): number
 | 
			
		||||
  {
 | 
			
		||||
    return Math.sqrt(this.x * this.x + this.y * this.y)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lengthManhattan(): number
 | 
			
		||||
  {
 | 
			
		||||
    return Math.abs(this.x) + Math.abs(this.y)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  normalize(): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return this.divideScalar(this.length())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  angle(): number
 | 
			
		||||
  {
 | 
			
		||||
    // computes the angle in radians with respect to the positive x-axis
 | 
			
		||||
    let angle: number = Math.atan2(this.y, this.x)
 | 
			
		||||
    if (angle < 0)
 | 
			
		||||
      angle += 2 * Math.PI
 | 
			
		||||
    return angle
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  distanceTo(v: Vector2): number
 | 
			
		||||
  {
 | 
			
		||||
    return Math.sqrt(this.distanceToSquared(v))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  distanceToSquared(v: Vector2): number
 | 
			
		||||
  {
 | 
			
		||||
    const dx: number = this.x - v.x; const dy: number = this.y - v.y
 | 
			
		||||
    return dx * dx + dy * dy
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  distanceToManhattan(v: Vector2): number
 | 
			
		||||
  {
 | 
			
		||||
    return Math.abs(this.x - v.x) + Math.abs(this.y - v.y)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setLength(length: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return this.multiplyScalar(length / this.length())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lerp(v: Vector2, alpha: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x += (v.x - this.x) * alpha
 | 
			
		||||
    this.y += (v.y - this.y) * alpha
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lerpVectors(v1: Vector2, v2: Vector2, alpha: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    return this.subVectors(v2, v1).multiplyScalar(alpha).add(v1)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  equals(v: Vector2): boolean
 | 
			
		||||
  {
 | 
			
		||||
    return ((v.x === this.x) && (v.y === this.y))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fromArray(array: Float32Array | number[], offset: number = 0): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    this.x = array[offset]
 | 
			
		||||
    this.y = array[offset + 1]
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toArray(array: Float32Array | number[] = [], offset: number = 0): Float32Array | number[]
 | 
			
		||||
  {
 | 
			
		||||
    array[offset] = this.x
 | 
			
		||||
    array[offset + 1] = this.y
 | 
			
		||||
    return array
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fromAttribute(attribute: any, index: number, offset: number = 0): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    index = index * attribute.itemSize + offset
 | 
			
		||||
    this.x = attribute.array[index]
 | 
			
		||||
    this.y = attribute.array[index + 1]
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rotateAround(center: Vector2, angle: number): Vector2
 | 
			
		||||
  {
 | 
			
		||||
    const c: number = Math.cos(angle); const s: number = Math.sin(angle)
 | 
			
		||||
    const x: number = this.x - center.x
 | 
			
		||||
    const y: number = this.y - center.y
 | 
			
		||||
    this.x = x * c - y * s + center.x
 | 
			
		||||
    this.y = x * s + y * c + center.y
 | 
			
		||||
    return this
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1625
									
								
								samples/handleAbility/common/base/CAD.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1625
									
								
								samples/handleAbility/common/base/CAD.ts
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										126
									
								
								samples/handleAbility/common/base/StringFormat.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								samples/handleAbility/common/base/StringFormat.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,126 @@
 | 
			
		||||
export class StringFormat
 | 
			
		||||
{
 | 
			
		||||
  /**
 | 
			
		||||
   * 对Date的扩展,将 Date 转化为指定格式的String
 | 
			
		||||
   * 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 个占位符
 | 
			
		||||
   * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
 | 
			
		||||
   * eg:
 | 
			
		||||
   * (new Date()).pattern("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
 | 
			
		||||
   * (new Date()).pattern("yyyy-MM-dd E HH:mm:ss") ==> 2009-03-10 二 20:09:04
 | 
			
		||||
   * (new Date()).pattern("yyyy-MM-dd EE hh:mm:ss") ==> 2009-03-10 周二 08:09:04
 | 
			
		||||
   * (new Date()).pattern("yyyy-MM-dd EEE hh:mm:ss") ==> 2009-03-10 星期二 08:09:04
 | 
			
		||||
   * (new Date()).pattern("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
 | 
			
		||||
   */
 | 
			
		||||
  static Date(date: Date, fmt): string
 | 
			
		||||
  {
 | 
			
		||||
    let o = {
 | 
			
		||||
      'M+': date.getMonth() + 1, // 月份
 | 
			
		||||
      'd+': date.getDate(), // 日
 | 
			
		||||
      'h+': date.getHours() % 12 == 0 ? 12 : date.getHours() % 12, // 小时
 | 
			
		||||
      'H+': date.getHours(), // 小时
 | 
			
		||||
      'm+': date.getMinutes(), // 分
 | 
			
		||||
      's+': date.getSeconds(), // 秒
 | 
			
		||||
      'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
 | 
			
		||||
      'S': date.getMilliseconds(), // 毫秒
 | 
			
		||||
    }
 | 
			
		||||
    let week = {
 | 
			
		||||
      0: '/u65e5',
 | 
			
		||||
      1: '/u4e00',
 | 
			
		||||
      2: '/u4e8c',
 | 
			
		||||
      3: '/u4e09',
 | 
			
		||||
      4: '/u56db',
 | 
			
		||||
      5: '/u4e94',
 | 
			
		||||
      6: '/u516d',
 | 
			
		||||
    }
 | 
			
		||||
    if (/(y+)/.test(fmt))
 | 
			
		||||
    {
 | 
			
		||||
      fmt = fmt.replace(RegExp.$1, (`${date.getFullYear()}`).substr(4 - RegExp.$1.length))
 | 
			
		||||
    }
 | 
			
		||||
    if (/(E+)/.test(fmt))
 | 
			
		||||
    {
 | 
			
		||||
      fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '/u661f/u671f' : '/u5468') : '') + week[`${date.getDay()}`])
 | 
			
		||||
    }
 | 
			
		||||
    for (let k in o)
 | 
			
		||||
    {
 | 
			
		||||
      if (new RegExp(`(${k})`).test(fmt))
 | 
			
		||||
      {
 | 
			
		||||
        fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : ((`00${o[k]}`).substr((`${o[k]}`).length)))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return fmt
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 返回数字的固定小数点 123.00  */
 | 
			
		||||
  static number(value: number, bit: number): string
 | 
			
		||||
  {
 | 
			
		||||
    return value?.toFixed(bit).toString()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static toFixed(value: number, bit: number = 3): number
 | 
			
		||||
  {
 | 
			
		||||
    let tt = 10 ** bit
 | 
			
		||||
    return Math.round(value * tt) / tt
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static filterIllegalChar(str: string): string
 | 
			
		||||
  {
 | 
			
		||||
    // var pattern = new RegExp("[/:*?'<>|\\]");
 | 
			
		||||
    // var rs = "";
 | 
			
		||||
    // for (var i = 0; i < str.length; i++)
 | 
			
		||||
    // {
 | 
			
		||||
    //     rs = rs + str.substr(i, 1).replace(pattern, '');
 | 
			
		||||
    // }
 | 
			
		||||
    // return rs;
 | 
			
		||||
 | 
			
		||||
    let rt = str.replace(/\\/g, '').replace(/\:/g, '').replace(/\*/g, '').replace(/\?/g, '').replace(/\"/g, '').replace(/\</g, '').replace(/\>/g, '').replace(/\|/g, '').replace(/\'/g, '')
 | 
			
		||||
    return rt
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 文本格式化 */
 | 
			
		||||
  static format(str: string, ...args: any[]): string
 | 
			
		||||
  {
 | 
			
		||||
    let data = args
 | 
			
		||||
    let tmpl = str
 | 
			
		||||
    for (const item of tmpl.matchAll(/\{(.+?)\}/g))
 | 
			
		||||
    {
 | 
			
		||||
      let parts = item[1].split(',').map(i => i.trim())
 | 
			
		||||
      let index = Number(parts[0])
 | 
			
		||||
      let arg = data[index]
 | 
			
		||||
 | 
			
		||||
      let val = (arg || '').toString() // 默认
 | 
			
		||||
 | 
			
		||||
      if (arg instanceof Date) // 日期
 | 
			
		||||
      {
 | 
			
		||||
        let fm = 'MM-dd HH;mm'
 | 
			
		||||
        if (parts.length > 1)
 | 
			
		||||
        {
 | 
			
		||||
          fm = parts[1]
 | 
			
		||||
        }
 | 
			
		||||
        val = this.Date(arg, fm)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (parts.length > 1 && parts[1][0] === '#')
 | 
			
		||||
      {
 | 
			
		||||
        // {2,#3} -> 数字 转成3位  001,...023,
 | 
			
		||||
        val = val.padStart(Number(parts[1].substring(1)), '0')
 | 
			
		||||
      }
 | 
			
		||||
      tmpl = tmpl.replace(item[0], val)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return tmpl
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 实现右对齐,右边填充 (25,4,'*') => '25**' */
 | 
			
		||||
  static PadEnd(num: number, totalWidth: number, paddingChar: string): string
 | 
			
		||||
  {
 | 
			
		||||
    let str = num.toString()
 | 
			
		||||
    return str.padEnd(totalWidth, paddingChar[0])
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** 实现右对齐,左边填充 (25,4,'0') => '0025' */
 | 
			
		||||
  static PadStart(num: number, totalWidth: number, paddingChar: string): string
 | 
			
		||||
  {
 | 
			
		||||
    let str = num.toString()
 | 
			
		||||
    return str.padStart(totalWidth, paddingChar[0])
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										381
									
								
								samples/handleAbility/common/core/Path.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								samples/handleAbility/common/core/Path.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,381 @@
 | 
			
		||||
import { Box2 } from '../Box2.js'  
 | 
			
		||||
import { clipperCpp } from '../ClipperCpp.js'
 | 
			
		||||
import type { NestFiler } from '../Filer.js'
 | 
			
		||||
import type { Point } from '../Point.js' 
 | 
			
		||||
import { equaln } from '../Util.js'  
 | 
			
		||||
import { Vector2 } from '../Vector2.js' 
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 轮廓路径类
 | 
			
		||||
 * 可以求NFP,和保存NFPCahce
 | 
			
		||||
 * 因为NFP结果是按照最低点移动的,所以将点旋转后,按照盒子将点移动到0点.
 | 
			
		||||
 */
 | 
			
		||||
export class Path
 | 
			
		||||
{
 | 
			
		||||
  Id: number
 | 
			
		||||
  Points: Point[]
 | 
			
		||||
  OutsideNFPCache: { [key: number]: Point[][] } = {}
 | 
			
		||||
  InsideNFPCache: { [key: number]: Point[][] } = {}
 | 
			
		||||
 | 
			
		||||
  constructor(public OrigionPoints?: Point[], rotation: number = 0)
 | 
			
		||||
  {
 | 
			
		||||
    if (OrigionPoints)
 | 
			
		||||
      this.Init(OrigionPoints, rotation)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Origion: Path
 | 
			
		||||
  // 点表在旋转后的原始最小点.使用这个点将轮廓移动到0点
 | 
			
		||||
  OrigionMinPoint: Vector2
 | 
			
		||||
  Rotation: number
 | 
			
		||||
 | 
			
		||||
  Size: Vector2// 序列化
 | 
			
		||||
  private Init(origionPoints: Point[], rotation: number)
 | 
			
		||||
  {
 | 
			
		||||
    this.Rotation = rotation
 | 
			
		||||
    if (rotation === 0)
 | 
			
		||||
      this.Points = origionPoints.map((p) => { return { ...p } })
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      let c = Math.cos(rotation)
 | 
			
		||||
      let s = Math.sin(rotation)
 | 
			
		||||
 | 
			
		||||
      let npts: Point[] = []
 | 
			
		||||
      for (let p of origionPoints)
 | 
			
		||||
      {
 | 
			
		||||
        let x = p.x
 | 
			
		||||
        let y = p.y
 | 
			
		||||
        const x1 = x * c - y * s
 | 
			
		||||
        const y1 = x * s + y * c
 | 
			
		||||
        npts.push({ x: x1, y: y1 })
 | 
			
		||||
      }
 | 
			
		||||
      this.Points = npts
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let box = new Box2()
 | 
			
		||||
    let v2 = new Vector2()
 | 
			
		||||
    for (let p of this.Points)
 | 
			
		||||
    {
 | 
			
		||||
      v2.x = p.x
 | 
			
		||||
      v2.y = p.y
 | 
			
		||||
      box.expandByPoint(v2)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.OrigionMinPoint = box.min
 | 
			
		||||
    this.Size = box.max.sub(box.min)
 | 
			
		||||
 | 
			
		||||
    for (let p of this.Points)
 | 
			
		||||
    {
 | 
			
		||||
      p.x -= box.min.x
 | 
			
		||||
      p.y -= box.min.y
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetNFPs(path: Path, outside: boolean): Point[][]
 | 
			
		||||
  {
 | 
			
		||||
    // 寻找内轮廓时,面积应该比本path小,这个判断移交给使用者自己判断
 | 
			
		||||
    // if (!outside && this.Area < path.Area) return [];
 | 
			
		||||
    let nfps = clipperCpp.lib.minkowskiSumPath(this.BigIntPoints, path.MirrorPoints, true)
 | 
			
		||||
 | 
			
		||||
    // 必须删除自交,否则将会出错
 | 
			
		||||
    nfps = clipperCpp.lib.simplifyPolygons(nfps)
 | 
			
		||||
    nfps = nfps.filter((nfp) =>
 | 
			
		||||
    {
 | 
			
		||||
      let area = Area(nfp)
 | 
			
		||||
      // if (area > 1) return outside;//第一个不一定是外轮廓,但是面积为正时肯定为外轮廓 (因为使用了简化多段线,所以这个代码已经不能有了)
 | 
			
		||||
      if (Math.abs(area) < 10)
 | 
			
		||||
        return false// 应该不用在移除这个了
 | 
			
		||||
 | 
			
		||||
      let { x, y } = nfp[0]
 | 
			
		||||
      if (outside)
 | 
			
		||||
      {
 | 
			
		||||
        if (this.Area > path.Area)
 | 
			
		||||
        {
 | 
			
		||||
          let p = { x: path.InPoint.x + x, y: path.InPoint.y + y }
 | 
			
		||||
          if (p.x < 0 || p.y < 0 || p.x > this.BigSize.x || p.y > this.BigSize.y)
 | 
			
		||||
            return true
 | 
			
		||||
          let dir = clipperCpp.lib.pointInPolygon(p, this.BigIntPoints)
 | 
			
		||||
          return dir === 0
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          let p = { x: this.InPoint.x - x, y: this.InPoint.y - y }
 | 
			
		||||
          if (p.x < 0 || p.y < 0 || p.x > path.BigSize.x || p.y > path.BigSize.y)
 | 
			
		||||
            return true
 | 
			
		||||
          let dir = clipperCpp.lib.pointInPolygon(p, path.BigIntPoints)
 | 
			
		||||
          return dir === 0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        let p = { x: path.InPoint.x + x, y: path.InPoint.y + y }
 | 
			
		||||
        if (p.x < 0 || p.y < 0 || p.x > this.BigSize.x || p.y > this.BigSize.y)
 | 
			
		||||
          return false
 | 
			
		||||
        let dir = clipperCpp.lib.pointInPolygon(p, this.BigIntPoints)
 | 
			
		||||
        return dir === 1
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    return nfps
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetOutsideNFP(path: Path): Point[][]
 | 
			
		||||
  {
 | 
			
		||||
    let nfps = this.OutsideNFPCache[path.Id]
 | 
			
		||||
    if (nfps)
 | 
			
		||||
      return nfps
 | 
			
		||||
 | 
			
		||||
    if (this.IsRect && path.IsRect)
 | 
			
		||||
    {
 | 
			
		||||
      let [ax, ay] = [this.Size.x * 1e4, this.Size.y * 1e4]
 | 
			
		||||
      let [bx, by] = [path.Size.x * 1e4, path.Size.y * 1e4]
 | 
			
		||||
      nfps = [[
 | 
			
		||||
        { x: -bx, y: -by },
 | 
			
		||||
        { x: ax, y: -by },
 | 
			
		||||
        { x: ax, y: ay },
 | 
			
		||||
        { x: -bx, y: ay },
 | 
			
		||||
      ]]
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
      nfps = this.GetNFPs(path, true)
 | 
			
		||||
    this.OutsideNFPCache[path.Id] = nfps
 | 
			
		||||
    // 虽然有这种神奇的特性,但是好像并不会提高性能。
 | 
			
		||||
    // path.OutsideNFPCache[this.id] = (this, nfps.map(nfp =>
 | 
			
		||||
    // {
 | 
			
		||||
    //     return nfp.map(p =>
 | 
			
		||||
    //     {
 | 
			
		||||
    //         return { x: -p.x, y: -p.y };
 | 
			
		||||
    //     });
 | 
			
		||||
    // }));
 | 
			
		||||
    return nfps
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  GetInsideNFP(path: Path): Point[][]
 | 
			
		||||
  {
 | 
			
		||||
    if (path.Area > this.Area)
 | 
			
		||||
      return
 | 
			
		||||
    let nfp = this.InsideNFPCache[path.Id]
 | 
			
		||||
    if (nfp)
 | 
			
		||||
      return nfp
 | 
			
		||||
 | 
			
		||||
    let nfps: Point[][]
 | 
			
		||||
    if (this.IsRect)
 | 
			
		||||
    {
 | 
			
		||||
      let [ax, ay] = [this.Size.x * 1e4, this.Size.y * 1e4]
 | 
			
		||||
      let [bx, by] = [path.Size.x * 1e4, path.Size.y * 1e4]
 | 
			
		||||
 | 
			
		||||
      let l = ax - bx
 | 
			
		||||
      let h = ay - by
 | 
			
		||||
 | 
			
		||||
      const MinNumber = 200// 清理的数值是100,所以200是可以接受的, 200=0.020问题不大(过盈配合)
 | 
			
		||||
      if (l < -MinNumber || h < -MinNumber)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
      if (l < MinNumber)
 | 
			
		||||
        l = MinNumber
 | 
			
		||||
      else
 | 
			
		||||
        l += MinNumber
 | 
			
		||||
 | 
			
		||||
      if (h < MinNumber)
 | 
			
		||||
        h = MinNumber
 | 
			
		||||
      else
 | 
			
		||||
        h += MinNumber
 | 
			
		||||
 | 
			
		||||
      nfps = [[
 | 
			
		||||
        { x: 0, y: 0 },
 | 
			
		||||
        { x: l, y: 0 },
 | 
			
		||||
        { x: l, y: h },
 | 
			
		||||
        { x: 0, y: h },
 | 
			
		||||
      ]]
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
      nfps = this.GetNFPs(path, false)
 | 
			
		||||
 | 
			
		||||
    if (path.Id !== undefined)
 | 
			
		||||
      this.InsideNFPCache[path.Id] = nfps
 | 
			
		||||
    return nfps
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _InPoint: Point
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 用这个点来检测是否在Path内部
 | 
			
		||||
   */
 | 
			
		||||
  private get InPoint()
 | 
			
		||||
  {
 | 
			
		||||
    if (this._InPoint)
 | 
			
		||||
      return this._InPoint
 | 
			
		||||
    let mp = { x: (this.Points[0].x + this.Points[1].x) / 2, y: (this.Points[0].y + this.Points[1].y) / 2 }
 | 
			
		||||
    let normal = new Vector2(this.Points[1].x - this.Points[0].x, this.Points[1].y - this.Points[0].y).normalize()
 | 
			
		||||
    // [normal.x, normal.y] = [normal.y, -normal.x];
 | 
			
		||||
    mp.x -= normal.y
 | 
			
		||||
    mp.y += normal.x
 | 
			
		||||
 | 
			
		||||
    mp.x *= 1e4
 | 
			
		||||
    mp.y *= 1e4
 | 
			
		||||
    this._InPoint = mp
 | 
			
		||||
    return mp
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _BigIntPoints: Point[]
 | 
			
		||||
  get BigIntPoints()
 | 
			
		||||
  {
 | 
			
		||||
    if (this._BigIntPoints)
 | 
			
		||||
      return this._BigIntPoints
 | 
			
		||||
    this._BigIntPoints = this.Points.map((p) =>
 | 
			
		||||
    {
 | 
			
		||||
      return {
 | 
			
		||||
        x: Math.round(p.x * 1e4),
 | 
			
		||||
        y: Math.round(p.y * 1e4),
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    return this._BigIntPoints
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _BigSize: Vector2
 | 
			
		||||
  get BigSize()
 | 
			
		||||
  {
 | 
			
		||||
    if (this._BigSize)
 | 
			
		||||
      return this._BigSize
 | 
			
		||||
    this._BigSize = new Vector2(this.Size.x * 1e4, this.Size.y * 1e4)
 | 
			
		||||
    return this._BigSize
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _MirrorPoints: Point[]
 | 
			
		||||
  get MirrorPoints()
 | 
			
		||||
  {
 | 
			
		||||
    if (!this._MirrorPoints)
 | 
			
		||||
      this._MirrorPoints = this.BigIntPoints.map((p) =>
 | 
			
		||||
      {
 | 
			
		||||
        return { x: -p.x, y: -p.y }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
    return this._MirrorPoints
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _BoundingBox: Box2
 | 
			
		||||
  get BoundingBox()
 | 
			
		||||
  {
 | 
			
		||||
    if (!this._BoundingBox)
 | 
			
		||||
     {
 | 
			
		||||
      this._BoundingBox = new Box2(new Vector2(), this.Size)
 | 
			
		||||
     }
 | 
			
		||||
    return this._BoundingBox
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected _Area: number
 | 
			
		||||
  get Area()
 | 
			
		||||
  {
 | 
			
		||||
    if (this._Area === undefined)
 | 
			
		||||
      this._Area = Area(this.Points)
 | 
			
		||||
    return this._Area
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  set Area(a: number)
 | 
			
		||||
  {
 | 
			
		||||
    this._Area = a
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _IsRect: boolean
 | 
			
		||||
  get IsRect()
 | 
			
		||||
  {
 | 
			
		||||
    if (this._IsRect === undefined)
 | 
			
		||||
    {
 | 
			
		||||
      let s = this.BoundingBox.getSize(new Vector2())
 | 
			
		||||
      this._IsRect = equaln(this.Area, s.x * s.y, 1)
 | 
			
		||||
    }
 | 
			
		||||
    return this._IsRect
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ReadFile(file: NestFiler): void
 | 
			
		||||
  {
 | 
			
		||||
    let ver = file.Read()
 | 
			
		||||
    this.Id = file.Read()
 | 
			
		||||
    let arr = file.Read()
 | 
			
		||||
    this.Points = []
 | 
			
		||||
    for (let i = 0; i < arr.length; i += 2)
 | 
			
		||||
    {
 | 
			
		||||
      let p = { x: arr[i], y: arr[i + 1] }
 | 
			
		||||
      this.Points.push(p)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.Size = new Vector2(file.Read(), file.Read())
 | 
			
		||||
    this._Area = file.Read()
 | 
			
		||||
    let id = file.Read()
 | 
			
		||||
    if (id !== -1)
 | 
			
		||||
    {
 | 
			
		||||
      this.Origion = id
 | 
			
		||||
      this.Rotation = file.Read()
 | 
			
		||||
      this.OrigionMinPoint = new Vector2(file.Read(), file.Read())
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  WriteFile(file: NestFiler): void
 | 
			
		||||
  {
 | 
			
		||||
    file.Write(1)// ver
 | 
			
		||||
    file.Write(this.Id)
 | 
			
		||||
    let arr: number[] = []
 | 
			
		||||
    for (let p of this.Points)
 | 
			
		||||
      arr.push(p.x, p.y)
 | 
			
		||||
    file.Write(arr)
 | 
			
		||||
 | 
			
		||||
    file.Write(this.Size.x)
 | 
			
		||||
    file.Write(this.Size.y)
 | 
			
		||||
    file.Write(this._Area)
 | 
			
		||||
    if (this.Origion && this.Origion.Id)
 | 
			
		||||
    {
 | 
			
		||||
      // 如果有原始的id,则传递它,以便后续进行NFP复用.
 | 
			
		||||
      file.Write(this.Origion.Id)
 | 
			
		||||
      file.Write(this.Rotation)
 | 
			
		||||
      file.Write(this.OrigionMinPoint.x)
 | 
			
		||||
      file.Write(this.OrigionMinPoint.y)
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
      file.Write(-1)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 点表面积
 | 
			
		||||
export function Area(pts: Point[]): number
 | 
			
		||||
{
 | 
			
		||||
  let cnt = pts.length
 | 
			
		||||
  if (cnt < 3)
 | 
			
		||||
    return 0
 | 
			
		||||
  let a = 0
 | 
			
		||||
  for (let i = 0, j = cnt - 1; i < cnt; ++i)
 | 
			
		||||
  {
 | 
			
		||||
    a += (pts[j].x + pts[i].x) * (pts[j].y - pts[i].y)
 | 
			
		||||
    j = i
 | 
			
		||||
  }
 | 
			
		||||
  return -a * 0.5
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 平移点表,返回新点表
 | 
			
		||||
 */
 | 
			
		||||
export function TranslatePath(pts: Point[], p: Point): Point[]
 | 
			
		||||
{
 | 
			
		||||
  return pts.map((px) =>
 | 
			
		||||
  {
 | 
			
		||||
    return { x: p.x + px.x, y: p.y + px.y }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function TranslatePath_Self(pts: Point[], mx: number, my: number): Point[]
 | 
			
		||||
{
 | 
			
		||||
  for (let pt of pts)
 | 
			
		||||
  {
 | 
			
		||||
    pt.x += mx
 | 
			
		||||
    pt.y += my
 | 
			
		||||
  }
 | 
			
		||||
  return pts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 缩放点表,返回原始点表
 | 
			
		||||
export function PathScale(pts: Point[], scale: number): Point[]
 | 
			
		||||
{
 | 
			
		||||
  for (let p of pts)
 | 
			
		||||
  {
 | 
			
		||||
    p.x *= scale
 | 
			
		||||
    p.y *= scale
 | 
			
		||||
  }
 | 
			
		||||
  return pts
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										0
									
								
								samples/handleAbility/common/工具类.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								samples/handleAbility/common/工具类.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										202
									
								
								samples/handleAbility/knifeHelper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								samples/handleAbility/knifeHelper.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
import { _knifeType, Knife, SideModel } from "../confClass";
 | 
			
		||||
import { knifeData,knifeData1,knifeData2 } from "../demoKnives";
 | 
			
		||||
/** 刀库 管理刀具*/
 | 
			
		||||
export class KnifeHelper {
 | 
			
		||||
    knifeList: Knife[]
 | 
			
		||||
    constructor(_knifeList) {
 | 
			
		||||
        
 | 
			
		||||
        this.knifeList.push(knifeData);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (Array.isArray(_knifeList) && _knifeList.length >0 ) {
 | 
			
		||||
            this.loadKnifeList(_knifeList)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadKnifeList(_knifeList) {
 | 
			
		||||
        this.knifeList = _knifeList || []
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 根据刀名称获取造型刀 */
 | 
			
		||||
    getModelKnifeByName(name: string): Knife | undefined {
 | 
			
		||||
        return this.knifeList.find(t => t.knifeName.trim() == name.trim() && t.isModelingKnife());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 根据半径获取造型刀 */
 | 
			
		||||
    getModelKnifeByRadius(radius: number, depth: number): Knife | undefined {
 | 
			
		||||
        return this.knifeList.find(t => t.isModelingKnife() && Math.abs(t.diameter - 2 * radius) < 0.02 && t.length >= depth - 0.01);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 获取合适的开料刀作为造型刀 */
 | 
			
		||||
    getFittedModelKnife(radius: number, depth: number): Knife | null {
 | 
			
		||||
        const knives = this.knifeList.filter(t => t.isModelingKnife() && t.length > depth - 0.01 && t.diameter / 2 < radius - 0.02);
 | 
			
		||||
        if (knives) {
 | 
			
		||||
            knives.sort((a, b) => b.diameter - a.diameter);     //按刀直径倒序排序
 | 
			
		||||
            return knives[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 根据厚度获取开料刀 */
 | 
			
		||||
    getKnifeByThickness(thickness: number): Knife | undefined {
 | 
			
		||||
        return this.knifeList.find(t => t.length >= thickness);
 | 
			
		||||
    }
 | 
			
		||||
    /** 通用 找刀具 根据查询条件 */
 | 
			
		||||
    getKnifeByParams(params: _knifeType) {
 | 
			
		||||
        let knife: Knife | null = null
 | 
			
		||||
 | 
			
		||||
        if (params) {
 | 
			
		||||
            let tempKnifeList: Knife[] = [...this.knifeList] // []
 | 
			
		||||
            let keys = Object.keys(params)
 | 
			
		||||
            if (keys.length > 0) {
 | 
			
		||||
                keys.forEach(key => {
 | 
			
		||||
                    if (Array.isArray(params[key]) && key == 'ability') {
 | 
			
		||||
                        // 进来的应该是ability  是数组  判断刀的能力
 | 
			
		||||
                        for (const arrItem of params[key]) {
 | 
			
		||||
                            let _knifeList = this.knifeList.filter(e => e.ability.includes(arrItem))
 | 
			
		||||
                            _knifeList.forEach(k => {
 | 
			
		||||
                                if (!this.KnifeIsInKnifeList(k, tempKnifeList)) {
 | 
			
		||||
                                    tempKnifeList.push(k)
 | 
			
		||||
                                }
 | 
			
		||||
                            })
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (['string', 'number'].includes(typeof (params[key]))) {
 | 
			
		||||
                        if (params && params[key] && typeof (params[key]) == 'number') {
 | 
			
		||||
 | 
			
		||||
                            if (key == 'length') {
 | 
			
		||||
                                tempKnifeList = tempKnifeList.filter(e => e[key] >= params[key])
 | 
			
		||||
                            } else {
 | 
			
		||||
                                tempKnifeList = tempKnifeList.filter(e => e[key] == params[key])
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                if (tempKnifeList.length > 0) {
 | 
			
		||||
                    knife = tempKnifeList[0]
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                console.log('传入的查询条件 没有参数')
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return knife
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 判断 刀是否在 刀列表内
 | 
			
		||||
     * true  存在
 | 
			
		||||
     * false 不存在
 | 
			
		||||
     */
 | 
			
		||||
    KnifeIsInKnifeList(_knife: Knife, _knifeList: Knife[]) {
 | 
			
		||||
        let k = _knifeList.find(e => _knife.axleId == e.axleId
 | 
			
		||||
            && _knife.diameter == e.diameter
 | 
			
		||||
            && _knife.knifeId == e.knifeId)
 | 
			
		||||
 | 
			
		||||
        if (k) {
 | 
			
		||||
            return true
 | 
			
		||||
        } else {
 | 
			
		||||
            return false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    getSideModelKnife(model: SideModel): Knife | undefined | null {
 | 
			
		||||
        if (model.knifeName && model.knifeName.trim() != '') {
 | 
			
		||||
            return this.getModelKnifeByName(model.knifeName.trim());
 | 
			
		||||
        }
 | 
			
		||||
        //获取刀半径(直径)和长度与造型定义的刀半径和深度一致的刀
 | 
			
		||||
        const knife = this.getSideModelKnifeByRadius(model.knifeRadius, model.depth, model.direction);
 | 
			
		||||
        if (knife) return knife;
 | 
			
		||||
        //获取刀半径(直径)小于造型定义的刀半径、长度不小于造型深度的刀
 | 
			
		||||
        return this.getFittedSideModelKnife(model.knifeRadius, model.depth, model.direction);
 | 
			
		||||
    }
 | 
			
		||||
    /** 获取合适的开料刀作为造型刀 */
 | 
			
		||||
    getFittedSideModelKnife(radius: number, depth: number, direction): Knife | null {
 | 
			
		||||
        let processFaceArr: number[] = [];
 | 
			
		||||
        processFaceArr.push(5)
 | 
			
		||||
        switch (direction) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                processFaceArr.push(4)
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:
 | 
			
		||||
                processFaceArr.push(2)
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                processFaceArr.push(3)
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 3:
 | 
			
		||||
 | 
			
		||||
                processFaceArr.push(1)
 | 
			
		||||
                break;
 | 
			
		||||
            case 10:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 11:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 12:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 13:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        const knives = this.knifeList.filter(t => t.isModelingKnife() && t.length > depth - 0.01 && t.diameter / 2 < radius - 0.02 && processFaceArr.includes(t.processFace));
 | 
			
		||||
        if (knives) {
 | 
			
		||||
            knives.sort((a, b) => b.diameter - a.diameter);     //按刀直径倒序排序
 | 
			
		||||
            return knives[0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    /** 根据半径获取造型刀 */
 | 
			
		||||
    // direction 0 下 1右 2上 3左 10 左下斜  11 右下斜 12右上斜  13 左上斜
 | 
			
		||||
    // processFace: number // 加工面 2024 11 21 : 	正面	0 	反面	1 	左侧面	2 右侧面	3 上侧面	4 	下侧面	5 任意	6
 | 
			
		||||
    getSideModelKnifeByRadius(radius: number, depth: number, direction): Knife | undefined {
 | 
			
		||||
 | 
			
		||||
        let processFaceArr: number[] = [];
 | 
			
		||||
        processFaceArr.push(5)
 | 
			
		||||
        switch (direction) {
 | 
			
		||||
            case 0:
 | 
			
		||||
                processFaceArr.push(4)
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:
 | 
			
		||||
                processFaceArr.push(2)
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                processFaceArr.push(3)
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 3:
 | 
			
		||||
 | 
			
		||||
                processFaceArr.push(1)
 | 
			
		||||
                break;
 | 
			
		||||
            case 10:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 11:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 12:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            case 13:
 | 
			
		||||
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        let knife = this.knifeList.find(t => t.isModelingKnife() && Math.abs(t.diameter - 2 * radius) < 0.02 && t.length >= depth - 0.01 && processFaceArr.includes(t.processFace))
 | 
			
		||||
 | 
			
		||||
        return knife;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								samples/handleAbility/基础功能目录.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								samples/handleAbility/基础功能目录.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
# 基础功能目录
 | 
			
		||||
 该目录下的功能函数尽量不包含业务逻辑 
 | 
			
		||||
							
								
								
									
										262
									
								
								samples/moduleManager/module1.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								samples/moduleManager/module1.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,262 @@
 | 
			
		||||
import { ProcessorModule } from "../../src/device";
 | 
			
		||||
import { confItem, PlaceBlock, PlaceBlockDetail, PlaceMaterial } from "../confClass";
 | 
			
		||||
import { Big_bang, ComposingType, LineType, WorkerItemType, xbang } from "../handleAbility/RectOptimizeWorker/bang";
 | 
			
		||||
import resData from './res1.json'
 | 
			
		||||
import {Worker} from 'worker_threads'
 | 
			
		||||
 
 | 
			
		||||
/** 模块  新优化
 | 
			
		||||
 * 
 | 
			
		||||
 * input  入参
 | 
			
		||||
 * 
 | 
			
		||||
 * ProcessorModule<any, any>这里的any 可以自定义 根据组件需求
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export const RectOptimizeMachineModule: ProcessorModule<any, any> = {
 | 
			
		||||
    moduleName: "RectOptimizeMachine",
 | 
			
		||||
    config: {
 | 
			
		||||
        workerList: [],
 | 
			
		||||
        placeStyle: 1,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setConfig(config) {
 | 
			
		||||
        this.config = { ...this.config, ...config };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    before(input) {
 | 
			
		||||
        // console.log(`优化前要做的事情`, input);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    process(input, next, context) {
 | 
			
		||||
        let { blockList, materialList } = input
 | 
			
		||||
        let bList: any = []
 | 
			
		||||
        let pm = materialList[0]
 | 
			
		||||
        blockList.map(e => {
 | 
			
		||||
            if (e.goodsId == pm.goodsId) {
 | 
			
		||||
                bList[e.blockNo] = e
 | 
			
		||||
                // let detail = blockDetailList.find(x => x.blockId == e.blockId)
 | 
			
		||||
                // // 处理可能出现的异常  --没有板明细
 | 
			
		||||
                // if (!Reflect.has(bList[e.blockNo], 'blockDetail')) {
 | 
			
		||||
                //     bList[e.blockNo].blockDetail = new PlaceBlockDetail(detail)
 | 
			
		||||
                // }
 | 
			
		||||
                // bList[e.blockNo].isTurnFaceToPlace = !this.getDoFace(bList[e.blockNo], this.processMode())
 | 
			
		||||
                // 是否翻面后续处理
 | 
			
		||||
                bList[e.blockNo].isTurnFaceToPlace = true
 | 
			
		||||
                if (Array.isArray(pm.blockList)) {
 | 
			
		||||
 | 
			
		||||
                    pm.blockList.push(e)
 | 
			
		||||
                } else {
 | 
			
		||||
                    pm.blockList = [e]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        pm.cutBorder = this.cutBoardBorder
 | 
			
		||||
        pm.cutKnifeGap = this.blockKnifeLineSpacing
 | 
			
		||||
 | 
			
		||||
        /** 小板 */
 | 
			
		||||
        let bans: xbang[] = []
 | 
			
		||||
 | 
			
		||||
        // 实际开料大板的列表
 | 
			
		||||
        let big_Bang: Big_bang[] = []
 | 
			
		||||
        let big_BangSL: number[] = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        let border = this.cutBoardBorder
 | 
			
		||||
        let borderOff = (pm.diameter + pm.cutKnifeGap) / 2
 | 
			
		||||
 | 
			
		||||
        // 余料板 以及实际开料大板
 | 
			
		||||
        for (const cuttingBoard of pm.remainBoardList) {
 | 
			
		||||
            big_Bang.push({ w: cuttingBoard.width - border * 2 + borderOff * 2, l: cuttingBoard.length - border * 2 + borderOff * 2, x: border - borderOff, y: border - borderOff })
 | 
			
		||||
            big_BangSL.push(cuttingBoard.count || 999)
 | 
			
		||||
        }
 | 
			
		||||
        // big_Bang = []
 | 
			
		||||
        // big_BangSL = []
 | 
			
		||||
        // 母板 兜底的
 | 
			
		||||
        big_Bang.push({ w: pm.width - border * 2 + borderOff * 2, l: pm.length - border * 2 + borderOff * 2, x: border - borderOff, y: border - borderOff })
 | 
			
		||||
        // 生成小板
 | 
			
		||||
        for (let key in bList) {
 | 
			
		||||
            let b = bList[key]
 | 
			
		||||
 | 
			
		||||
            let bid = b.blockNo
 | 
			
		||||
 | 
			
		||||
            let width = b.placeFullWidth
 | 
			
		||||
            let length = b.placeFullLength
 | 
			
		||||
            let line = toLine(b.texture)
 | 
			
		||||
 | 
			
		||||
            bans.push({
 | 
			
		||||
                l: length,
 | 
			
		||||
                w: width,
 | 
			
		||||
                line,
 | 
			
		||||
                face: toface(b),
 | 
			
		||||
                id: bid,
 | 
			
		||||
                bno: b.blockNo,
 | 
			
		||||
                holeFaceCount: 3,
 | 
			
		||||
                isRect: !b.isUnRegular,
 | 
			
		||||
                hasHole: false,
 | 
			
		||||
                isdtwosided: true,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let bestCount = 0
 | 
			
		||||
        if (bans.length == 0) // 没有板了
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (context.CallBack) {
 | 
			
		||||
                let best = []
 | 
			
		||||
                let yl: Big_bang[] = []
 | 
			
		||||
                let fit = 0
 | 
			
		||||
                let resObj = {
 | 
			
		||||
                    data: { bList, best, yl, fit, bans, width: pm.width, length: pm.length, bestCount: bestCount++, pm },
 | 
			
		||||
                    info: {
 | 
			
		||||
                        times: -1,
 | 
			
		||||
                        type: 'noBan'
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                context.CallBack(resObj)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        let xyhcs = 50
 | 
			
		||||
        if (bans.length > 1000) { xyhcs = 40 }
 | 
			
		||||
        else if (bans.length > 1500) { xyhcs = 30 }
 | 
			
		||||
        else if (bans.length > 2000) { xyhcs = 25 }
 | 
			
		||||
        else if (bans.length > 4000) { xyhcs = 20 }
 | 
			
		||||
        else if (bans.length > 6000) { xyhcs = 15 }
 | 
			
		||||
        else if (bans.length > 10000) { xyhcs = 10 }
 | 
			
		||||
        else if (bans.length > 15000) { xyhcs = 5 }
 | 
			
		||||
        else if (bans.length > 20000) { xyhcs = 1 }
 | 
			
		||||
        let isDoubleFaceBlockFirst = this.isDoubleFaceBlockFirst // 双面加工排前面
 | 
			
		||||
        let gap = this.blockKnifeLineSpacing
 | 
			
		||||
        // this.bestfit = 0
 | 
			
		||||
 | 
			
		||||
        /** 这里要做个多线程  测试环境下 这部分有问题 暂时略过 */
 | 
			
		||||
        for (let j = 0; j < 1; j++) {
 | 
			
		||||
         
 | 
			
		||||
            // let w = new Worker(new URL('../handleAbility/RectOptimizeWorker/RectOptimizeWorker.worker', import.meta.url), { type: 'module' })
 | 
			
		||||
            let w= new Worker('../handleAbility/RectOptimizeWorker/RectOptimizeWorker.worker')
 | 
			
		||||
            const data = {
 | 
			
		||||
                type: 'start',
 | 
			
		||||
                data: [bans, big_Bang, big_BangSL, xyhcs, isDoubleFaceBlockFirst, gap, this.isRectPlace, this.isDoubleFaceBlockInRemain],
 | 
			
		||||
            }
 | 
			
		||||
            let item: WorkerItemType = {
 | 
			
		||||
                w: w,
 | 
			
		||||
                goodsId: pm.goodsId,
 | 
			
		||||
                pm,
 | 
			
		||||
                status: 'start'
 | 
			
		||||
            }
 | 
			
		||||
            if (this.config.workerList.findIndex(e => e.goodsId == item.goodsId) == -1) {
 | 
			
		||||
                this.config.workerList.push(item)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            let workItem = this.config.workerList.find(e => e.goodsId == pm.goodsId)
 | 
			
		||||
            if (workItem && workItem != undefined) {
 | 
			
		||||
                workItem.w?.postMessage(data)
 | 
			
		||||
 | 
			
		||||
                workItem.w.onmessage = async (d) => {
 | 
			
		||||
                    let [best, yl, fit, info] = d.data as [any[], Big_bang[], number, any]
 | 
			
		||||
 | 
			
		||||
                    switch (info.type) {
 | 
			
		||||
                        case 'loop':
 | 
			
		||||
                            let resObj = {
 | 
			
		||||
                                data: { bList, best, yl, fit, bans, width: pm.width, length: pm.length, bestCount: bestCount++, pm },
 | 
			
		||||
                                info
 | 
			
		||||
                            }
 | 
			
		||||
                            if (context.CallBack) {
 | 
			
		||||
                                context.CallBack(resObj)
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 'stop':
 | 
			
		||||
                            console.error('stop =》dataHandleBase', info, this.config.workerList)
 | 
			
		||||
                            this.terminateWorker({ goodsId: pm.goodsId })
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 'isStop':
 | 
			
		||||
                            // console.error('isStop', info)
 | 
			
		||||
                            break;
 | 
			
		||||
                        default:
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (context.CallBack) {
 | 
			
		||||
            context.CallBack(resData)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return next ? next(input) : input;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    after(result) {
 | 
			
		||||
        console.log(`优化后要做的事情`,);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onError(error) {
 | 
			
		||||
        console.log('出错了哦', error);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function toLine(texture): LineType {
 | 
			
		||||
    if (texture == 0)
 | 
			
		||||
        return LineType.Positive
 | 
			
		||||
    if (texture == 1)
 | 
			
		||||
        return LineType.CanReversal
 | 
			
		||||
    return LineType.Reverse
 | 
			
		||||
}
 | 
			
		||||
/** 小板加工面:Positive=0正面  Reverse=1反面  Arbitrary=2任意 */
 | 
			
		||||
function toface(block: PlaceBlock): ComposingType {
 | 
			
		||||
    let turnF = block.isTurnFaceToPlace
 | 
			
		||||
    // if (this.isTurnFaceToPlace)
 | 
			
		||||
    if (true)
 | 
			
		||||
        turnF = !turnF
 | 
			
		||||
    if (!turnF)
 | 
			
		||||
        return ComposingType.Positive
 | 
			
		||||
    return ComposingType.Reverse
 | 
			
		||||
}
 | 
			
		||||
/** 配置列表 */
 | 
			
		||||
export const confList: confItem[] = [
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isCutProcess',
 | 
			
		||||
        label: '开料机(雕刻机)加工',
 | 
			
		||||
        value: true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isCutAndCNCProcess',
 | 
			
		||||
        label: '开料机CNC组合',
 | 
			
		||||
        value: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isCustomized',
 | 
			
		||||
        label: '定制加工',
 | 
			
		||||
        value: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'cutBoardBorder',
 | 
			
		||||
        label: '总修边宽度',
 | 
			
		||||
        value: 3
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'blockKnifeLineSpacing',
 | 
			
		||||
        label: '刀路间距',
 | 
			
		||||
        value: 0
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isDoubleFaceBlockFirst',
 | 
			
		||||
        label: '双面开料优先排版',
 | 
			
		||||
        value: true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        key: 'isRectPlace',
 | 
			
		||||
        label: '新优化规则排版',
 | 
			
		||||
        value: false
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        // yuLiaoBoardDo2FaceBlock
 | 
			
		||||
        key: 'isDoubleFaceBlockInRemain',
 | 
			
		||||
        label: '余料板允许排入双面加工的小板',
 | 
			
		||||
        value: true
 | 
			
		||||
    },
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										29615
									
								
								samples/moduleManager/res1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29615
									
								
								samples/moduleManager/res1.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -4,7 +4,7 @@ import { ConfigBase } from "./models/config";
 | 
			
		||||
/**
 | 
			
		||||
 * 加工处理器上下文
 | 
			
		||||
 */
 | 
			
		||||
export interface ProcessorContext<TInput = any, TOutput = any, TConfig extends ConfigBase = ConfigBase> {
 | 
			
		||||
export interface ProcessorContext<TInput, TOutput, TConfig extends ConfigBase> {
 | 
			
		||||
    /**
 | 
			
		||||
     * 输入数据
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										248
									
								
								src/device.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								src/device.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,248 @@
 | 
			
		||||
 | 
			
		||||
// 回调函数类型定义
 | 
			
		||||
type ProcessCallback<T, R> = (
 | 
			
		||||
    input: T,
 | 
			
		||||
    next: (input: T) => R | Promise<R>,
 | 
			
		||||
    context?: any
 | 
			
		||||
) => R | Promise<R>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// 模块配置类型
 | 
			
		||||
type ModuleConfig = Record<string, any>;
 | 
			
		||||
 | 
			
		||||
// 回调函数类型
 | 
			
		||||
type ModuleCallback<T, R> = (result: R, input?: T) => void | Promise<void>;
 | 
			
		||||
 | 
			
		||||
// 扩展后的功能模块接口
 | 
			
		||||
export interface ProcessorModule<T, R> {
 | 
			
		||||
    // 主处理函数(可回调)
 | 
			
		||||
    process?: ProcessCallback<T, R>;
 | 
			
		||||
 | 
			
		||||
    // 直接是处理函数(无回调)
 | 
			
		||||
    handle?: (input: T, next?: (input: T) => R | Promise<R>, context?: Record<string, any>) => R | Promise<R>;
 | 
			
		||||
 | 
			
		||||
    // 模块名称(用于标识和排序)
 | 
			
		||||
    moduleName?: string;
 | 
			
		||||
 | 
			
		||||
    // 模块配置
 | 
			
		||||
    config?: ModuleConfig;
 | 
			
		||||
 | 
			
		||||
    getConfigList?: () => ModuleConfig
 | 
			
		||||
    // 设置配置的方法
 | 
			
		||||
    setConfig?: (config: ModuleConfig) => void;
 | 
			
		||||
 | 
			
		||||
    // 前置回调(在模块处理前执行)
 | 
			
		||||
    before?: ModuleCallback<T, R>;
 | 
			
		||||
 | 
			
		||||
    // 后置回调(在模块处理后执行)
 | 
			
		||||
    after?: ModuleCallback<T, R>;
 | 
			
		||||
 | 
			
		||||
    // 错误处理回调
 | 
			
		||||
    onError?: (error: unknown, input?: T) => void | Promise<void>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Processor<T, R> {
 | 
			
		||||
    // 注册模块
 | 
			
		||||
    use(module: ProcessorModule<T, R> | ProcessorModule<T, R>[]): this;
 | 
			
		||||
 | 
			
		||||
    // 调整模块顺序
 | 
			
		||||
    reorderModules(moduleNames: string[]): this;
 | 
			
		||||
 | 
			
		||||
    // 执行处理流程
 | 
			
		||||
    process(input: T): Promise<R>;
 | 
			
		||||
 | 
			
		||||
    // 获取当前模块列表
 | 
			
		||||
    getModules(): ProcessorModule<T, R>[];
 | 
			
		||||
 | 
			
		||||
    // 新增方法:更新模块配置
 | 
			
		||||
    updateModuleConfig(moduleName: string, config: ModuleConfig): this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 处理器集合接口
 | 
			
		||||
export interface ProcessorCollection<T, R> {
 | 
			
		||||
    // 注册处理器
 | 
			
		||||
    registerProcessor(name: string, processor: Processor<T, R>): this;
 | 
			
		||||
 | 
			
		||||
    // 切换当前处理器
 | 
			
		||||
    useProcessor(name: string): Processor<T, R>;
 | 
			
		||||
 | 
			
		||||
    // 获取处理器
 | 
			
		||||
    getProcessor(name: string): Processor<T, R> | undefined;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 流程管理器  处理器内组件的执行流程管理器
 | 
			
		||||
 * 
 | 
			
		||||
 * 负责管理 组件执行顺序  和执行模块
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export class StepControllerProcessor<T, R> implements Processor<T, R> {
 | 
			
		||||
    private modules: ProcessorModule<T, R>[] = [];
 | 
			
		||||
    private modulesMap = new Map<string, ProcessorModule<T, R>>();
 | 
			
		||||
 | 
			
		||||
    use(module: ProcessorModule<T, R> | ProcessorModule<T, R>[]): this {
 | 
			
		||||
        const modules = Array.isArray(module) ? module : [module];
 | 
			
		||||
 | 
			
		||||
        modules.forEach(m => {
 | 
			
		||||
            if (m.moduleName) {
 | 
			
		||||
                this.modulesMap.set(m.moduleName, m);
 | 
			
		||||
            }
 | 
			
		||||
            this.modules.push(m);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reorderModules(moduleNames: string[]): this {
 | 
			
		||||
        const orderedModules = moduleNames
 | 
			
		||||
            .map(name => this.modulesMap.get(name))
 | 
			
		||||
            .filter(Boolean) as ProcessorModule<T, R>[];
 | 
			
		||||
 | 
			
		||||
        const remainingModules = this.modules.filter(
 | 
			
		||||
            m => !m.moduleName || !moduleNames.includes(m.moduleName)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        this.modules = [...orderedModules, ...remainingModules];
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateModuleConfig(moduleName: string, config: ModuleConfig): this {
 | 
			
		||||
        const module = this.modulesMap.get(moduleName);
 | 
			
		||||
        if (module && module.setConfig) {
 | 
			
		||||
            module.setConfig(config);
 | 
			
		||||
        } else if (module) {
 | 
			
		||||
            module.config = { ...module.config, ...config };
 | 
			
		||||
        }
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async executeModule(
 | 
			
		||||
        module: ProcessorModule<T, R>,
 | 
			
		||||
        input: T,
 | 
			
		||||
        next: (input: T) => Promise<R>,
 | 
			
		||||
        context: Record<string, any>
 | 
			
		||||
    ): Promise<R> {
 | 
			
		||||
        try {
 | 
			
		||||
            // 执行前置回调
 | 
			
		||||
            if (module.before) {
 | 
			
		||||
                await module.before(input, input);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 执行主处理逻辑(支持两种风格)
 | 
			
		||||
            let result: R;
 | 
			
		||||
 | 
			
		||||
            if (module.process) {
 | 
			
		||||
                // 回调风格
 | 
			
		||||
                const processResult = module.process(input, next, context);
 | 
			
		||||
                result = processResult instanceof Promise ? await processResult : processResult;
 | 
			
		||||
            } else if (module.handle) {
 | 
			
		||||
                // 传统风格
 | 
			
		||||
                const handleResult = module.handle(input, next, context);
 | 
			
		||||
                result = handleResult instanceof Promise ? await handleResult : handleResult;
 | 
			
		||||
            } else {
 | 
			
		||||
                // 默认直接调用 next
 | 
			
		||||
                result = await next(input);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 执行后置回调
 | 
			
		||||
            if (module.after) {
 | 
			
		||||
                await module.after(result, input);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // 执行错误处理
 | 
			
		||||
            if (module.onError) {
 | 
			
		||||
                await module.onError(error, input);
 | 
			
		||||
                // 即使出错也继续流程(除非抛出)
 | 
			
		||||
                return await next(input);
 | 
			
		||||
            }
 | 
			
		||||
            throw error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    async process(input: T): Promise<R> {
 | 
			
		||||
        if (this.modules.length === 0) {
 | 
			
		||||
            throw new Error("No modules registered");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let currentIndex = 0;
 | 
			
		||||
        const modules = this.modules;
 | 
			
		||||
        const context: Record<string, any> = {};
 | 
			
		||||
 | 
			
		||||
        const executeNext = async (currentInput: T): Promise<R> => {
 | 
			
		||||
            const currentModule = modules[currentIndex++];
 | 
			
		||||
            if (!currentModule) {
 | 
			
		||||
                return currentInput as unknown as R;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 创建 next 函数
 | 
			
		||||
            const next = async (nextInput: T): Promise<R> => {
 | 
			
		||||
                return executeNext(nextInput);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return this.executeModule(currentModule, currentInput, next, context);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return executeNext(input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // async process(input: T): Promise<R> {
 | 
			
		||||
    //     if (this.modules.length === 0) {
 | 
			
		||||
    //         throw new Error("No modules registered");
 | 
			
		||||
    //     }
 | 
			
		||||
 | 
			
		||||
    //     let currentIndex = 0;
 | 
			
		||||
    //     const modules = this.modules;
 | 
			
		||||
    //     const context: Record<string, any> = {}; // 共享上下文
 | 
			
		||||
 | 
			
		||||
    //     const executeNext = async (currentInput: T): Promise<R> => {
 | 
			
		||||
    //         const currentModule = modules[currentIndex++];
 | 
			
		||||
    //         if (!currentModule) {
 | 
			
		||||
    //             return currentInput as unknown as R;
 | 
			
		||||
    //         }
 | 
			
		||||
 | 
			
		||||
    //         try {
 | 
			
		||||
    //             // 执行前置回调
 | 
			
		||||
    //             if (currentModule.before) {
 | 
			
		||||
    //                 await currentModule.before(currentInput, currentInput);
 | 
			
		||||
    //             }
 | 
			
		||||
 | 
			
		||||
    //             // 执行主处理函数
 | 
			
		||||
    //             const next = async (nextInput: T): Promise<R> => {
 | 
			
		||||
    //                 return executeNext(nextInput);
 | 
			
		||||
    //             };
 | 
			
		||||
 | 
			
		||||
    //             let result: R;
 | 
			
		||||
    //             const processResult = currentModule.process(currentInput, next, context);
 | 
			
		||||
 | 
			
		||||
    //             if (processResult instanceof Promise) {
 | 
			
		||||
    //                 result = await processResult;
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 result = processResult;
 | 
			
		||||
    //             }
 | 
			
		||||
 | 
			
		||||
    //             // 执行后置回调
 | 
			
		||||
    //             if (currentModule.after) {
 | 
			
		||||
    //                 await currentModule.after(result, currentInput);
 | 
			
		||||
    //             }
 | 
			
		||||
 | 
			
		||||
    //             return result;
 | 
			
		||||
    //         } catch (error) {
 | 
			
		||||
    //             // 执行错误处理
 | 
			
		||||
    //             if (currentModule.onError) {
 | 
			
		||||
    //                 await currentModule.onError(error, currentInput);
 | 
			
		||||
    //             } else {
 | 
			
		||||
    //                 throw error; // 如果没有错误处理,则向上抛出
 | 
			
		||||
    //             }
 | 
			
		||||
 | 
			
		||||
    //             // 根据错误处理结果决定是否继续
 | 
			
		||||
    //             return currentInput as unknown as R;
 | 
			
		||||
    //         }
 | 
			
		||||
    //     };
 | 
			
		||||
 | 
			
		||||
    //     return executeNext(input);
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    getModules(): ProcessorModule<T, R>[] {
 | 
			
		||||
        return [...this.modules];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,10 +2,4 @@ export * from './base';
 | 
			
		||||
export * from './parsers';
 | 
			
		||||
export * from './models/config';
 | 
			
		||||
export * from './models/knife';
 | 
			
		||||
export * from './models/file';
 | 
			
		||||
export * from './models/processors/rectLayout';
 | 
			
		||||
export * from './models/processors/cutOrder';
 | 
			
		||||
export * from './models/processItem';
 | 
			
		||||
export * from './models/processors/modelProcessPoints'
 | 
			
		||||
export * from './models/processors/cutPoint'
 | 
			
		||||
export * from './models/processors/holeToModel'
 | 
			
		||||
export * from './models/file';
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
 * 配置基类,下划线开头的变量不会被序列化
 | 
			
		||||
 */
 | 
			
		||||
export class ConfigBase {
 | 
			
		||||
    readonly name: string = '';
 | 
			
		||||
    readonly version: string = '1.0.0';
 | 
			
		||||
    name: string = '';
 | 
			
		||||
    version:string = '1.0.0';
 | 
			
		||||
    [key: string]: any;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 加载反序列化数据
 | 
			
		||||
     * @param data 
 | 
			
		||||
     */
 | 
			
		||||
    load(data: Record<string, unknown>) {
 | 
			
		||||
        for (const key of Object.getOwnPropertyNames(this).filter(i => i[0] !== '_')) {
 | 
			
		||||
            if (data[key] != undefined) {
 | 
			
		||||
    load(data:Record<string,unknown>){
 | 
			
		||||
        for (const key of Object.getOwnPropertyNames(this).filter(i=>i[0]!=='_')) {
 | 
			
		||||
            if(data[key]!=undefined){
 | 
			
		||||
                this[key] = data[key];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -22,7 +22,7 @@ export class ConfigBase {
 | 
			
		||||
     * 序列化json方法
 | 
			
		||||
     * @returns 
 | 
			
		||||
     */
 | 
			
		||||
    toJson() {
 | 
			
		||||
        return JSON.stringify(this, (k, v) => k[0] == '_' ? undefined : v);
 | 
			
		||||
    toJson(){
 | 
			
		||||
        return JSON.stringify(this,(k,v)=>k[0]=='_'?undefined:v);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								src/models/layout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/models/layout.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,96 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @file 优化布局相关的数据模型,包含优化输入、输出、大板、小板以及各类枚举
 | 
			
		||||
 * @todo 目前仅适配了矩形优化,后续还需要对数据结构进行扩展
 | 
			
		||||
 * @author CZY
 | 
			
		||||
 * @since 0.1.8
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/** 优化小板输入 */
 | 
			
		||||
export interface LayoutBlock {
 | 
			
		||||
    /** 小板ID */
 | 
			
		||||
    id: number;
 | 
			
		||||
    /** 长 */
 | 
			
		||||
    length: number;
 | 
			
		||||
    /** 宽 */
 | 
			
		||||
    width: number;
 | 
			
		||||
    /** 纹路类型 */
 | 
			
		||||
    waveType: WaveType;
 | 
			
		||||
    /** 排版面类型 */
 | 
			
		||||
    composingType: ComposingType;
 | 
			
		||||
    /** 孔洞类型 */
 | 
			
		||||
    holeType: HoleType;
 | 
			
		||||
    /** 是否为矩形板 */
 | 
			
		||||
    isRect?: boolean;
 | 
			
		||||
    /** 是否需要双面加工 */
 | 
			
		||||
    isdtwosided?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 优化大板输入 */
 | 
			
		||||
export interface LayoutBoard {
 | 
			
		||||
    length: number;
 | 
			
		||||
    width: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 纹路类型 */
 | 
			
		||||
export enum WaveType {
 | 
			
		||||
    /** 正纹 */
 | 
			
		||||
    Positive = 0,
 | 
			
		||||
    /** 反纹 */
 | 
			
		||||
    Reverse = 1,
 | 
			
		||||
    /** 可翻转 */
 | 
			
		||||
    CanReversal = 2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 排版面 */
 | 
			
		||||
export enum ComposingType {
 | 
			
		||||
    /** 正面 */
 | 
			
		||||
    Positive = 0,
 | 
			
		||||
    Reverse = 1,   //反面
 | 
			
		||||
    Arbitrary = 2  //任意
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 孔类型 */
 | 
			
		||||
export enum HoleType {
 | 
			
		||||
    /** 没有孔 */
 | 
			
		||||
    None = 0,
 | 
			
		||||
    /** 正面 */
 | 
			
		||||
    Positive = 1,
 | 
			
		||||
    /** 反面 */
 | 
			
		||||
    Reverse = 2,
 | 
			
		||||
    /** 正反皆有 */
 | 
			
		||||
    Two = 3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 布局大板 */
 | 
			
		||||
export interface LayoutResultBoard {
 | 
			
		||||
    id: string;
 | 
			
		||||
    /** 大板宽度 */
 | 
			
		||||
    boardWidth: number;
 | 
			
		||||
    /** 大板高度 */
 | 
			
		||||
    boardHeight: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 布局小板 */
 | 
			
		||||
export interface LayoutResultBlock {
 | 
			
		||||
    id: string;
 | 
			
		||||
    /** x坐标 */
 | 
			
		||||
    x: number;
 | 
			
		||||
    /** y坐标 */
 | 
			
		||||
    y: number;
 | 
			
		||||
    /** 宽度 */
 | 
			
		||||
    width: number;
 | 
			
		||||
    /** 高度 */
 | 
			
		||||
    height: number;
 | 
			
		||||
    /** 纹路类型 */
 | 
			
		||||
    waveType: WaveType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 优化布局结果 */
 | 
			
		||||
export type LayoutResult = {
 | 
			
		||||
    /** 大板列表 */
 | 
			
		||||
    boards: LayoutResultBoard[],
 | 
			
		||||
    /** 小板列表,其一维与boards长度对应,二维为小板列表 */
 | 
			
		||||
    blocks: LayoutResultBlock[][],
 | 
			
		||||
    /** 优化中被使用的余料大板,这个列表中的每一个元素代表使用了一片该规格的大板 */
 | 
			
		||||
    usedScrapBoard: LayoutResultBoard[];
 | 
			
		||||
};
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
 | 
			
		||||
export interface IPoint { x: number, y: number; }
 | 
			
		||||
/**
 | 
			
		||||
 * 加工数据
 | 
			
		||||
 */
 | 
			
		||||
export interface IProcessingItem {
 | 
			
		||||
    /**
 | 
			
		||||
     * 加工点数组
 | 
			
		||||
     */
 | 
			
		||||
    pts: IPoint[];
 | 
			
		||||
    /**
 | 
			
		||||
     * 凸度数组
 | 
			
		||||
     */
 | 
			
		||||
    buls: number[];
 | 
			
		||||
    /**
 | 
			
		||||
     * 加工深度
 | 
			
		||||
     */
 | 
			
		||||
    depth: number;
 | 
			
		||||
    /**
 | 
			
		||||
     * 半径
 | 
			
		||||
     */
 | 
			
		||||
    radius: number;
 | 
			
		||||
    /**
 | 
			
		||||
     * 刀ID
 | 
			
		||||
     */
 | 
			
		||||
    knifeId?: number;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 开料顺序(新)所使用的类型
 | 
			
		||||
 * 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { ConfigBase } from "../config";
 | 
			
		||||
 | 
			
		||||
/** 处理器入参 开料顺序(新)  
 | 
			
		||||
 * @author lx
 | 
			
		||||
 *
 | 
			
		||||
 *  注:20250730 暂无配置 留个位置
 | 
			
		||||
 */
 | 
			
		||||
export class CutorderConfig extends ConfigBase {
 | 
			
		||||
    // [key:string] : any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 处理器输入数据源 开料顺序(新)  
 | 
			
		||||
 */
 | 
			
		||||
export type CutOrderInput = {
 | 
			
		||||
    /**开料大板 宽 */
 | 
			
		||||
    boardWidth: number,
 | 
			
		||||
    /** 开料大板 高 */
 | 
			
		||||
    boardHeight: number,
 | 
			
		||||
    /** 刀头大小(含修边)  */
 | 
			
		||||
    gap: number,
 | 
			
		||||
    /** 小板数据集合 */
 | 
			
		||||
    blocks: CutorderInputBlock[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 处理器输出数据 开料顺序(新)  
 | 
			
		||||
 */
 | 
			
		||||
export type CutorderOutput = {
 | 
			
		||||
    blocks: CutorderoutputBlock[]
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 小板类型 输入
 | 
			
		||||
 */
 | 
			
		||||
export type CutorderInputBlock = {
 | 
			
		||||
    /** 小板唯一标识  */
 | 
			
		||||
    id: string | number,
 | 
			
		||||
    /** 排版长 */
 | 
			
		||||
    length: number,
 | 
			
		||||
    /** 排版宽 */
 | 
			
		||||
    width: number,
 | 
			
		||||
    /** 板件坐标X */
 | 
			
		||||
    x: number,
 | 
			
		||||
    /** 板件坐标y */
 | 
			
		||||
    y: number,
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * 小板类型 输出
 | 
			
		||||
 */
 | 
			
		||||
export type CutorderoutputBlock = CutorderInputBlock & {
 | 
			
		||||
    cutOrder: number
 | 
			
		||||
}
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
import { ConfigBase } from "../config"
 | 
			
		||||
import { IProcessingItem } from "../processItem"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 下刀点 入参
 | 
			
		||||
 */
 | 
			
		||||
export type CutPointInput = {
 | 
			
		||||
 | 
			
		||||
    /** (余料板异形点) 开料大板的开料轮廓数据 若没有则需要传 开料大板宽、高*/
 | 
			
		||||
    boardPointInfo?: IProcessingItem,
 | 
			
		||||
    /** 开料大板宽 若有 boardPointInfo 则不需要传 */
 | 
			
		||||
    boardWidth?: number,
 | 
			
		||||
    /** 开料大板长 若有 boardPointInfo 则不需要传 */
 | 
			
		||||
    boardLength?: number
 | 
			
		||||
    /** 小板数据集 */
 | 
			
		||||
    blocks?: CutPointInputBlock[]
 | 
			
		||||
}
 | 
			
		||||
/** 处理器输出---下刀点 */
 | 
			
		||||
export type CutPointOutput = {
 | 
			
		||||
    blocks: CutPointOutputBlock[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 小板类型 输入
 | 
			
		||||
 */
 | 
			
		||||
export type CutPointInputBlock = {
 | 
			
		||||
    /** 小板唯一标识  */
 | 
			
		||||
    id: string | number;
 | 
			
		||||
    /** 排版长 */
 | 
			
		||||
    length: number;
 | 
			
		||||
    /** 排版宽 */
 | 
			
		||||
    width: number;
 | 
			
		||||
    /** 板件坐标X */
 | 
			
		||||
    x: number;
 | 
			
		||||
    /** 板件坐标y */
 | 
			
		||||
    y: number;
 | 
			
		||||
    /** 开料顺序 */
 | 
			
		||||
    cutOrder: number;
 | 
			
		||||
    /** 
 | 
			
		||||
     * 板件轮廓 
 | 
			
		||||
     * 用以分析下刀点的板件轮廓 
 | 
			
		||||
     * */
 | 
			
		||||
    blockPoints: IProcessingItem
 | 
			
		||||
    /** 是否异形  true 是异形  false 矩形 */
 | 
			
		||||
    isUnRegular: boolean
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 小板类型 输出 */
 | 
			
		||||
export type CutPointOutputBlock = CutPointInputBlock & {
 | 
			
		||||
    /** 下刀点  板件轮廓的下标 */
 | 
			
		||||
    cutPointId?: number,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 下刀点配置 
 | 
			
		||||
 * 
 | 
			
		||||
 * 注:暂时没有配置项
 | 
			
		||||
 */
 | 
			
		||||
export declare class CutPointConfig extends ConfigBase {
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
import { ConfigBase } from "../config"
 | 
			
		||||
import { IProcessingItem } from "../processItem"
 | 
			
		||||
 | 
			
		||||
/** 处理器输入 -孔转造型*/
 | 
			
		||||
export type HoleToModelInput = {
 | 
			
		||||
    /** 孔信息 */
 | 
			
		||||
    holeData: HoleToModelProcessingItem[],
 | 
			
		||||
    /** 孔所在板件的优化后的坐标 X (可选)*/
 | 
			
		||||
    placeX?: number,
 | 
			
		||||
    /** 孔所在板件的优化后的坐标 Y (可选)*/
 | 
			
		||||
    placeY?: number,
 | 
			
		||||
    /** 孔所在板件的 厚度 */
 | 
			
		||||
    thickness: number
 | 
			
		||||
}
 | 
			
		||||
/** 处理器输出-- 获取造型在大板的刀路 */
 | 
			
		||||
export type HoleToModelOutput = {
 | 
			
		||||
    /** 孔转造型 后的 造型数据 */
 | 
			
		||||
    modelData: HoleToModelProcessingItem[],
 | 
			
		||||
    /** 未处理的孔数据 以及信息 */
 | 
			
		||||
    noHandleItem: noHandleItemType[]
 | 
			
		||||
}
 | 
			
		||||
export type noHandleItemType = {
 | 
			
		||||
    /** 未处理的孔信息 */
 | 
			
		||||
    holeData: HoleToModelProcessingItem,
 | 
			
		||||
    /** 未处理 说明 */
 | 
			
		||||
    info: string
 | 
			
		||||
}
 | 
			
		||||
/** 处理器配置-- 获取造型在大板的刀路  */
 | 
			
		||||
export declare class HoleToModelProcConfig extends ConfigBase {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
/** 加工项的类型  */
 | 
			
		||||
export type HoleToModelProcessingItem = IProcessingItem & {
 | 
			
		||||
    /** 使用刀具的刀半径 */
 | 
			
		||||
    knifeRadius: number
 | 
			
		||||
}
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
import { ConfigBase } from "../config"
 | 
			
		||||
import { IPoint } from "../processItem"
 | 
			
		||||
 | 
			
		||||
/** 处理器输入-- 获取造型在大板的刀路 */
 | 
			
		||||
export type ModelProcessPointsInput = {
 | 
			
		||||
    /** 小板数据 */
 | 
			
		||||
    block: ModelProcessPointsInputBlock,
 | 
			
		||||
    /** 小板的最终的放置位置 */
 | 
			
		||||
    targetPosition: PositionType
 | 
			
		||||
}
 | 
			
		||||
/** 处理器输入--小板 -- 获取造型在大板的刀路 */
 | 
			
		||||
export type ModelProcessPointsInputBlock = {
 | 
			
		||||
    /** 板件唯一标识 */
 | 
			
		||||
    id: string | number,
 | 
			
		||||
    /** 板件基于大板的 坐标X */
 | 
			
		||||
    x: number,
 | 
			
		||||
    /** 板件基于大板的 坐标y */
 | 
			
		||||
    y: number,
 | 
			
		||||
    /** 板件(小板)长 */
 | 
			
		||||
    length: number,
 | 
			
		||||
    /** 板件(小板)宽 */
 | 
			
		||||
    width: number,
 | 
			
		||||
    /** 造型数据 依据放置方式positionType 下的造型数据  默认为 依据放置方式positionType.FRONT 的造型数据 */
 | 
			
		||||
    models: ModelProcessItem[],
 | 
			
		||||
    /** 板件的原放置方式 默认为正面(0) 不传则为正面  原 placestyle*/
 | 
			
		||||
    positionType?: PositionType,
 | 
			
		||||
    /** 偏移值 */
 | 
			
		||||
    offsetInfo: OffsetInfo
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
/** 板件 上下左右 偏移值信息 */
 | 
			
		||||
export type OffsetInfo = {
 | 
			
		||||
    top: number,
 | 
			
		||||
    bottom: number,
 | 
			
		||||
    left: number,
 | 
			
		||||
    right: number,
 | 
			
		||||
}
 | 
			
		||||
/** 处理器输出--小板 -- 获取造型在大板的刀路 */
 | 
			
		||||
export type ModelProcessPointsOutputBlock = {
 | 
			
		||||
    /** 板件唯一标识 */
 | 
			
		||||
    id: string | number
 | 
			
		||||
    /** 放置方式 */
 | 
			
		||||
    positionType: PositionType
 | 
			
		||||
    /** 造型数据 */
 | 
			
		||||
    models: ModelProcessItem[]
 | 
			
		||||
    /** 板件(小板)长 */
 | 
			
		||||
    length: number,
 | 
			
		||||
    /** 板件(小板)宽 */
 | 
			
		||||
    width: number,
 | 
			
		||||
    /** 偏移值 */
 | 
			
		||||
    offsetInfo: OffsetInfo
 | 
			
		||||
}
 | 
			
		||||
/** 处理器输出-- 获取造型在大板的刀路 */
 | 
			
		||||
export type ModelProcessPointsOutput = {
 | 
			
		||||
    block: ModelProcessPointsOutputBlock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 处理器配置-- 获取造型在大板的刀路  暂无 */
 | 
			
		||||
export declare class ModelProcessPointsProcConfig extends ConfigBase {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** 造型类 */
 | 
			
		||||
export interface ModelProcessItem {
 | 
			
		||||
    /** 加工项唯一标识 */
 | 
			
		||||
    id?: string | number
 | 
			
		||||
    /**
 | 
			
		||||
    * 加工点数组
 | 
			
		||||
    */
 | 
			
		||||
    pts: IPoint[];
 | 
			
		||||
    /**
 | 
			
		||||
     * 凸度数组
 | 
			
		||||
     */
 | 
			
		||||
    buls: number[];
 | 
			
		||||
    /** 加工面 */
 | 
			
		||||
    face: FaceType
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 加工面 */
 | 
			
		||||
export enum FaceType {
 | 
			
		||||
    /** 正面 */
 | 
			
		||||
    FRONT = 0,
 | 
			
		||||
    /** 反面 */
 | 
			
		||||
    BACK = 1,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
/** 小板的放置方式 */
 | 
			
		||||
export enum PositionType {
 | 
			
		||||
    /** 正面 */
 | 
			
		||||
    FRONT = 0,
 | 
			
		||||
    /** 正面右转 */
 | 
			
		||||
    FRONT_TURN_RIGHT = 1,
 | 
			
		||||
    /** 正面后转 */
 | 
			
		||||
    FRONT_TURN_BACK = 2,
 | 
			
		||||
    /** 正面左转 */
 | 
			
		||||
    FRONT_TURN_LEFT = 3,
 | 
			
		||||
    /** 反面 */
 | 
			
		||||
    BACK = 4,
 | 
			
		||||
    /** 反面右转 */
 | 
			
		||||
    BACK_TURN_RIGHT = 5,
 | 
			
		||||
    /** 反面后转 */
 | 
			
		||||
    BACK_TURN_BACK = 6,
 | 
			
		||||
    /** 反面左转 */
 | 
			
		||||
    BACK_TURN_LEFT = 7,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** 行为类型  */
 | 
			
		||||
export type RotationAction = 'doNothing' | 'turnLeft' | 'turnRight' | 'turnAround'
 | 
			
		||||
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @file 矩形板件布局优化处理器使用的数据模型,包含优化输入、输出、大板、小板以及各类枚举
 | 
			
		||||
 * @todo 目前仅适配了矩形优化,后续还需要对数据结构进行扩展
 | 
			
		||||
 * @file 矩形板件布局优化处理器使用的数据模型
 | 
			
		||||
 * @since 0.1.8
 | 
			
		||||
 * @author CZY
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { ConfigBase } from "../config";
 | 
			
		||||
import { LayoutBlock, LayoutBoard, LayoutResult } from "../layout";
 | 
			
		||||
 | 
			
		||||
export interface RectLayoutProcInput {
 | 
			
		||||
    /** 小板列表 */
 | 
			
		||||
    blocks: RectLayoutBlock[];
 | 
			
		||||
    blocks: LayoutBlock[];
 | 
			
		||||
    /** 余料大板列表,可选,余料大板将会被优先优化,当余料大板被用尽时,则会使用配置中的大板尺寸进行优化 */
 | 
			
		||||
    scrapBoards?: Array<{
 | 
			
		||||
        /** 余料大板 */
 | 
			
		||||
        board: RectLayoutBoard,
 | 
			
		||||
        board: LayoutBoard,
 | 
			
		||||
        /** 大板张数 */
 | 
			
		||||
        count: number;
 | 
			
		||||
    }>;
 | 
			
		||||
@@ -30,101 +30,6 @@ export class RectLayoutProcConfig extends ConfigBase {
 | 
			
		||||
    doubleSidedFirst: boolean = false;
 | 
			
		||||
    /** 刀路间隙 */
 | 
			
		||||
    gap: number = 0;
 | 
			
		||||
    /** 运行标识 (其实改成方法更合适*/
 | 
			
		||||
    _runFlag: 'running' | 'stopped' | 'terminated' = 'running';
 | 
			
		||||
    /** 当出现优化结果时,进行回调 */
 | 
			
		||||
    _onMessage?: (e: LayoutResult) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export type RectLayoutProcOutput = never;
 | 
			
		||||
 | 
			
		||||
/** 矩形优化小板输入 */
 | 
			
		||||
export interface RectLayoutBlock {
 | 
			
		||||
    /** 小板ID */
 | 
			
		||||
    id: number;
 | 
			
		||||
    /** 长 */
 | 
			
		||||
    length: number;
 | 
			
		||||
    /** 宽 */
 | 
			
		||||
    width: number;
 | 
			
		||||
    /** 纹路类型 */
 | 
			
		||||
    waveType: WaveType;
 | 
			
		||||
    /** 排版面类型 */
 | 
			
		||||
    composingType: ComposingType;
 | 
			
		||||
    /** 孔洞类型 */
 | 
			
		||||
    holeType: HoleType;
 | 
			
		||||
    /** 是否为矩形板 */
 | 
			
		||||
    isRect?: boolean;
 | 
			
		||||
    /** 是否需要双面加工 */
 | 
			
		||||
    isdtwosided?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 矩形优化大板输入 */
 | 
			
		||||
export interface RectLayoutBoard {
 | 
			
		||||
    length: number;
 | 
			
		||||
    width: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 纹路类型 */
 | 
			
		||||
export enum WaveType {
 | 
			
		||||
    /** 正纹 */
 | 
			
		||||
    Positive = 0,
 | 
			
		||||
    /** 反纹 */
 | 
			
		||||
    Reverse = 1,
 | 
			
		||||
    /** 可翻转 */
 | 
			
		||||
    CanReversal = 2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 排版面 */
 | 
			
		||||
export enum ComposingType {
 | 
			
		||||
    /** 正面 */
 | 
			
		||||
    Positive = 0,
 | 
			
		||||
    Reverse = 1,   //反面
 | 
			
		||||
    Arbitrary = 2  //任意
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 孔类型 */
 | 
			
		||||
export enum HoleType {
 | 
			
		||||
    /** 没有孔 */
 | 
			
		||||
    None = 0,
 | 
			
		||||
    /** 正面 */
 | 
			
		||||
    Positive = 1,
 | 
			
		||||
    /** 反面 */
 | 
			
		||||
    Reverse = 2,
 | 
			
		||||
    /** 正反皆有 */
 | 
			
		||||
    Two = 3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 布局大板 */
 | 
			
		||||
export interface LayoutResultBoard {
 | 
			
		||||
    id: string;
 | 
			
		||||
    /** 大板高度 */
 | 
			
		||||
    boardLength: number;
 | 
			
		||||
    /** 大板宽度 */
 | 
			
		||||
    boardWidth: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 布局小板 */
 | 
			
		||||
export interface LayoutResultBlock {
 | 
			
		||||
    id: string;
 | 
			
		||||
    /** x坐标 */
 | 
			
		||||
    x: number;
 | 
			
		||||
    /** y坐标 */
 | 
			
		||||
    y: number;
 | 
			
		||||
    /** 高度 */
 | 
			
		||||
    length: number;
 | 
			
		||||
    /** 宽度 */
 | 
			
		||||
    width: number;
 | 
			
		||||
    /** 纹路类型 */
 | 
			
		||||
    waveType: WaveType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 优化布局结果 */
 | 
			
		||||
export type LayoutResult = {
 | 
			
		||||
    /** 大板列表 */
 | 
			
		||||
    boards: LayoutResultBoard[],
 | 
			
		||||
    /** 小板列表,其一维与boards长度对应,二维为小板列表 */
 | 
			
		||||
    blocks: LayoutResultBlock[][],
 | 
			
		||||
    /** 优化中被使用的余料大板,这个列表中的每一个元素代表使用了一片该规格的大板 */
 | 
			
		||||
    usedScrapBoard: LayoutResultBoard[];
 | 
			
		||||
};
 | 
			
		||||
export type RectLayoutProcOutput = LayoutResult;
 | 
			
		||||
@@ -1,126 +0,0 @@
 | 
			
		||||
import { Knife } from "../models/knife";
 | 
			
		||||
 | 
			
		||||
/** 基础G代码 */
 | 
			
		||||
export const GCode = {
 | 
			
		||||
    /**
 | 
			
		||||
     * 快速定位
 | 
			
		||||
     * @param X X 坐标
 | 
			
		||||
     * @param Y Y 坐标
 | 
			
		||||
     * @param Z Z 坐标
 | 
			
		||||
     * @param F 给进速度 mm/min
 | 
			
		||||
     * @example G0 X0 Y0 F25000
 | 
			
		||||
     */
 | 
			
		||||
    G0: 'G0',
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 直线插补
 | 
			
		||||
     * @param X X 坐标
 | 
			
		||||
     * @param Y Y 坐标
 | 
			
		||||
     * @param Z Z 坐标
 | 
			
		||||
     * @param F 给进速度 mm/min
 | 
			
		||||
     * @example G1 X0 Y0 F25000
 | 
			
		||||
     */
 | 
			
		||||
    G1: 'G1',
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 顺时针圆弧插补
 | 
			
		||||
     * @param X X 坐标
 | 
			
		||||
     * @param Y Y 坐标
 | 
			
		||||
     * @param Z Z 坐标
 | 
			
		||||
     * @param I (圆心坐标模式)从圆弧起点到圆心在X轴上的**增量距离**
 | 
			
		||||
     * @param J (圆心坐标模式)从圆弧起点到圆心在Y轴上的**增量距离**
 | 
			
		||||
     * @param K (圆心坐标模式)从圆弧起点到圆心在Z轴上的**增量距离**
 | 
			
		||||
     * @param R (圆弧半径模式)指定圆弧的半径
 | 
			
		||||
     * @example
 | 
			
		||||
     * ; 假设当前刀具在 (X10, Y0)
 | 
			
		||||
     * G2 X0 Y10 I-10 J0 F150 ; 顺时针圆弧到X0 Y10,圆心在(0,0) (10 + (-10) = 0, 0 + 0 = 0)
 | 
			
		||||
     * G2 X0 Y10 R10 F150 ; 顺时针圆弧到X0 Y10,半径10 (小于等于180度)
 | 
			
		||||
     */
 | 
			
		||||
    G2: 'G2',
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 逆时针圆弧插补
 | 
			
		||||
     * @param X X 坐标
 | 
			
		||||
     * @param Y Y 坐标
 | 
			
		||||
     * @param Z Z 坐标
 | 
			
		||||
     * @param I (圆心坐标模式)从圆弧起点到圆心在X轴上的**增量距离**
 | 
			
		||||
     * @param J (圆心坐标模式)从圆弧起点到圆心在Y轴上的**增量距离**
 | 
			
		||||
     * @param K (圆心坐标模式)从圆弧起点到圆心在Z轴上的**增量距离**
 | 
			
		||||
     * @param R (圆弧半径模式)指定圆弧的半径
 | 
			
		||||
     * @example
 | 
			
		||||
     * ; 假设当前刀具在 (X10, Y0)
 | 
			
		||||
     * G3 X0 Y-10 I-10 J0 F150 ; 逆时针圆弧到X0 Y-10,圆心在(0,0)
 | 
			
		||||
     * G3 X0 Y-10 R-10 F150 ; 逆时针圆弧到X0 Y-10,半径10 (大于180度)
 | 
			
		||||
     */
 | 
			
		||||
    G3: 'G3',
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
/**  */
 | 
			
		||||
export class GCodeParams {
 | 
			
		||||
    /** X坐标值 mm */
 | 
			
		||||
    x: number = 0;
 | 
			
		||||
    /** Y坐标值 mm */
 | 
			
		||||
    y: number = 0;
 | 
			
		||||
    /** Z坐标值 mm */
 | 
			
		||||
    z: number = 0;
 | 
			
		||||
    /** 使用圆弧指令(G2,G3)的圆弧半径模式(R)时,定义圆弧半径 */
 | 
			
		||||
    r: number = 0;
 | 
			
		||||
    /** 使用圆弧指令(G2,G3)的圆心坐标模式(I,J,K)时,定义圆心坐标在X轴上的增量距离 */
 | 
			
		||||
    i: number = 0;
 | 
			
		||||
    /** 使用圆弧指令(G2,G3)的圆心坐标模式(I,J,K)时,定义圆心坐标在Y轴上的增量距离 */
 | 
			
		||||
    j: number = 0;
 | 
			
		||||
    /** 给进速度(Feed Rate) mm/min */
 | 
			
		||||
    f: number = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/** 单次NC加工行为 */
 | 
			
		||||
export interface NcAction {
 | 
			
		||||
    readonly id: string;
 | 
			
		||||
    readonly type: NcActionType;
 | 
			
		||||
    readonly lineIndex: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type NcActionType = string;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * NC代码构建器接口,实现该接口来定义一个处理器用的NC代码构建器
 | 
			
		||||
 * @author CZY
 | 
			
		||||
 * @since 0.3.0
 | 
			
		||||
 * @version 0.1.0
 | 
			
		||||
 */
 | 
			
		||||
export interface INcWriter {
 | 
			
		||||
    get ncActions(): NcAction[];
 | 
			
		||||
 | 
			
		||||
    /** 添加G代码 */
 | 
			
		||||
    gCode(code: keyof typeof GCode, params: Partial<Pick<GCodeParams, 'x' | 'y' | 'z' | 'f'> & { bul: number; }>): void;
 | 
			
		||||
 | 
			
		||||
    /** 基于刀具实体执行换刀操作 */
 | 
			
		||||
    changeKnife(knife: Knife): void;
 | 
			
		||||
 | 
			
		||||
    /** 添加一行注释 */
 | 
			
		||||
    comment(content: string): void;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 记录一次NC加工操作
 | 
			
		||||
     * 
 | 
			
		||||
     * NC加工操作指的是一次完整的加工过程,例如一次排钻,一次造型切割,或是一次小板加工
 | 
			
		||||
     * 
 | 
			
		||||
     * 一次NC加工操作可能包含多行的GCode
 | 
			
		||||
     * 
 | 
			
		||||
     * 该方法旨在为NC文件提供记录加工步骤的能力
 | 
			
		||||
     * 
 | 
			
		||||
     * 返回本次NC操作的ID,此ID由内部生成
 | 
			
		||||
     * @param type NC操作类型
 | 
			
		||||
     * @returns 本次NC操作的ID
 | 
			
		||||
     */
 | 
			
		||||
    recordAction(type: NcActionType): string;
 | 
			
		||||
 | 
			
		||||
    /** 直接在末尾追加任意字符串 */
 | 
			
		||||
    append(str: string): void;
 | 
			
		||||
 | 
			
		||||
    /** 直接在末尾追加一行任意字符串 */
 | 
			
		||||
    appendLine(str: string): void;
 | 
			
		||||
 | 
			
		||||
    /** 返回当前构建的NC代码 */
 | 
			
		||||
    toString(): string;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user