feat:初步实现 自测验证种

This commit is contained in:
2025-08-07 17:35:47 +08:00
commit 04cb7558d0
13 changed files with 53034 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.tgz
node_modules/*
*.yaml
dist/*

111
README.md Normal file
View File

@@ -0,0 +1,111 @@
# @mes-processors/libs
这是一个用于处理MES制造执行系统相关工作流的处理器类库。
## 安装
在内网环境下执行以下脚本进行安装
```sh
pnpm add http://gitea.cf/MES-FE/mes-packages/releases/download/0.1/mes-processors-libs-0.1.0.tgz
```
> [!CAUTION]
> 在安装库之前,请确认以下信息:
>
> - 该库发布于内网gitea仓库的release中所以你需要提前在gitea中进行登录并确保你有该仓库的访问权限。
> - 库的版本需要手动进行控制,注意上述链接中的版本信息,在安装前需要主动修改版本号,请前往<http://gitea.cf/MES-FE/mes-packages/releases>来确认最新版本。
## 使用
该库提供了MES/iMES公用的处理器并已配置为导出项请参考以下Typescript代码进行使用
```ts
// 引入矩形优化处理器
import { RectLayoutProcConfig } from 'cut-abstractions';
import { RectLayoutProc } from '@mes-processors/libs';
// 实例化处理器
const proc = new RectLayoutProc();
// 构建上下文f proc.exec>[0] = {
input: testObj,
params: new RectLayoutProcConfig()
};
// 异步执行
const ctx: Parameters<typeo
await proc.exec(ctx);
// 从上下文对象中获取输出内容
console.log("RESULT: ", ctx.output);
```
## Q&A
### 运行某些处理器时出现404(Not Found)错误
**错误描述**
当执行处理器时,出现类似下面的错误:
```log
GET http://localhost:5173/node_modules/.vite/deps/assets/RectOptimizeMachine.worker-BO2fmpVH.js 404 (Not Found)
```
**根本原因**
该库中某些处理器使用了Web Worker来实现多线程异步处理例如矩形优化处理器
Web Worker为单独打包的资产文件但某些打包工具可能会对`node_modules`中的依赖进行预构建来提高性能如果Worker文件被视为了预构建的一部分就可能导致处理器无法正确地处理Worker文件的相对引用路径导致在运行时尝试从`node_modules/.vite/deps/assets/`这样的内部路径加载,而这个路径在实际部署或服务时是不存在的。
**解决方法**
在打包工具中对该库进行配置,禁用对该库的优化和预构建,以`vite`为例:
```ts
// vite.config.ts
...
optimizeDeps: {
exclude: ["@mes-processors/libs"] // 从optimizeDeps中排除该库
}
...
```
## 开发
### 发布并打包项目
```sh
pnpm build
pnpm pack
```
> [!NOTE]
> 发布前记得更改版本号
### 约定
**目录**
```
src
├── modules 项目模块分组
├── processors 处理器
└── utils 公用的工具类
```
**导出和打包**
- 编写的处理器请在`src/index.ts`中进行导出
- 编写的工具类请在`src/utils/index.ts`中进行导出
- 在打包时项目仅会对`src/index.ts`进行打包,工具类相关模块不会进行打包
- 关于打包相关明细请自行查看相关文件
- [package.json](package.json)
- [vite.config.ts](vite.config.ts)
> [!WARNING]
> 在该工作区中编写模块时,禁止使用绝对路径进行导入,禁止在`tsconfig.json`或`vite.config.ts`中添加"@"别名所有导入语句请使用相对路径进行引入否则会因monorepo内部导入混乱导致模块解析失败。
### 测试
项目使用[Vitest](http://vitest.dev/)作为单元测试框架若要对TS文件编写单元测试请在文件的同目录下创建`<文件名>.test.ts`文件并遵循Vitest规范编写单元测试。
要执行单元测试,请运行下面的命令:
```sh
pnpm test
```

38
package.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "@imes-modelprocesspoints/libs",
"version": "0.1.0",
"description": "",
"type": "module",
"scripts": {
"build": "vite build",
"test": "vitest",
"check": "tsc --noEmit --skipLibCheck -p tsconfig.app.json"
},
"files": [
"dist",
"package.json",
"src",
"tsconfig.json",
"tsconfig.app.json",
"tsconfig.node.json"
],
"exports": {
".": "./src/index.ts",
"./utils": "./src/utils/index.ts"
},
"dependencies": {
"cut-abstractions": "http://gitea.cf/MES-FE/cut-abstractions/releases/download/0.2/cut-abstractions-0.2.1.tgz"
},
"devDependencies": {
"@types/node": "^24.0.10",
"typescript": "~5.8.3",
"vite": "^7.0.0",
"vite-plugin-dts": "^4.5.4",
"vite-plugin-node-polyfills": "^0.24.0",
"vite-plugin-resolve": "^2.5.2",
"vitest": "^3.2.4"
},
"keywords": [],
"author": "",
"license": "ISC"
}

8
src/index.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* @package @mes-cutpoint/libs
* @author LX
* @description 工作流处理器类库,在这个文件中使用导出时,不要在路径中使用'@',否则模块无法加载
*/
// CutOrder
export * from "./processors/cutPoint/CutPoint";

View File

@@ -0,0 +1,375 @@
export class List<Item> extends Array<Item>
{
/** 返回符合条件的第一个元素 */
first(fn: (item: Item) => boolean): Item | null
{
if (this.length == 0)
return null
for (const item of this)
{
if (fn(item))
return item
}
return null
}
/** 返回符合条件的最后一元素 */
last(fn: (t: Item) => boolean): Item | null
{
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)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,532 @@
import { ConfigBase, ProcessorBase, ProcessorContext } from "cut-abstractions";
/**
* 获取造型在开料大板的坐标
*
* 输入的造型数据为 要处理的造型数据
*/
export class ModelProcessPointsProc extends ProcessorBase<ModelProcessPointsInput, ModelProcessPointsOutput, ModelProcessPointsProcConfig> {
get name(): string {
return 'ModelProcessPoints';
}
get version(): string {
return '1.0.0';
}
exec(context: ProcessorContext<ModelProcessPointsInput, ModelProcessPointsOutput, ModelProcessPointsProcConfig>): Promise<void> | void {
return new Promise(async (resolve, reject) => {
try {
if (context.input) {
if (context.input.block) {
// if (Array.isArray(context.input.block.models) && context.input.block.models.length > 0) {
/** 板件的坐标X */
let placeX = context.input.block.x
/** 板件的坐标Y */
let placeY = context.input.block.y
let output: ModelProcessPointsOutput = {
block: {
id: context.input.block.id,
positionType: context.input.block.positionType || 0,
models: [],
width: context.input.block.width,
length: context.input.block.length,
offsetInfo: context.input.block.offsetInfo
}
}
/**
* 提要:
* 1、这里的造型数据 都是 基于板件(小板)的正面的点阵数据
* 2、坐标转换 依据2个要素进行转换 【板件(小板)的优化坐标】 和 【板件(小板)的放置方式】
*
* 先处理放置方式 在处理 优化坐标 会简单很多
*/
let res = this.handleByPositionType(context.input.block, context.input.targetPosition)
let res1 = this.handleByPlaceXY(res.models, placeX, placeY)
res.models = res1
output.block = {
...output.block,
...res,
// positionType:context.input.targetPosition
}
context.output = output
resolve()
} else {
reject('ModelProcessPoints error: input.block is undefined;')
}
} else {
reject('ModelProcessPoints error: input is undefined;')
}
resolve()
} catch (error) {
reject(error);
}
});
}
/**
* 根据小板的放置方式进行偏移
*
* 提要:models内的造型数据 都是以小板正面 为基准
* 即 PositionType.FRONT
* 需要 以下几种情况
* 加工项所在的面 -- 2种 正 反
* 小板放置方式 -- 8 种 详见 PositionType 处理器入参 默认为 PositionType.FRONT
*
* 细节:
* 加工项 为正面 且放置方式为正面的 无需操作 直接输出,其它情况 都需要做处理
*
* 如果有 翻面的情况 弧度值需要取反
*
* 思考: 坐标转换的实质 其实 也就是以下几种行为
* 面与面的行为: 翻面a 面到 b 面
* 单面内的行为: 左转 右转 翻转
*
* 这里其实 可以有个流程
* 先判定 是不是要 翻面
* 然后再去 做 单面内的行为 相关的坐标转换 将问题集中在一个面的操作上
*
* PositionType XXX 转到 PositionType XX 无非就是 【左转 或 右转 或 翻转】 或者 先 翻面 然后 【左转 或 右转 或 翻转】
* 那么 就
* 有翻面 先 翻面 然后 再 【左| 右 | 翻】 转
*
* 注: 这里的操作 都依据绘图可视的X Y 轴进行处理
* block.width 对应 Y轴的值
* block.length 对应 x轴的值
*/
handleByPositionType(block: ModelProcessPointsInputBlock, targetPosition: PositionType) {
/** 造型输出的数据 */
let models: ModelProcessItem[] = []
let _width = block.width
let _length = block.length
/** 默认 正面 */
if (block.positionType == undefined) {
block.positionType = PositionType.FRONT
}
//#region 板件 进行分析 翻面 以及 行为
let face = this.getFace(block.positionType)
let targetFace = this.getFace(targetPosition)
if (face == -1) {
// 异常情况
console.log(`block-${block.id}:block.positionType is invalid`)
}
if (targetFace == -1) {
// 异常情况
console.log(`targetFace:targetFace is invalid`)
}
// 判定是否要翻面
const isTurn = !(face == targetFace)
/** 翻面后的 放置方式 */
let tempPosition = block.positionType
if(block.id =='25044454400'){
debugger
}
if (isTurn) {
tempPosition = this.getPositionAfterTurnFace(block.positionType)
}
// 获取行为 左|右|翻转|不操作
const action = this.getDir(tempPosition, targetPosition)
//#endregion
// 处理造型的部分
for (const i in block.models) {
let model = {...block.models[i]}
/** 如果要 翻面 那就先把面 翻过来 */
if (isTurn) {
// 翻面 x 不变 Y 变
model = this.change_turnFace(model, _width)
// 然后 弧度值要取反
model = this.reverseBuls(model)
}
/**
* 根据 行为标识 转换坐标
* 注: 左转 或者时右转 需要 转换 板件(小板)的 长宽值
*
*/
switch (action) {
case 'doNothing':
// 啥事也不做
break;
case 'turnAround':
// 翻转 x 变 y 也变
model = this.change_turnAround(model, _length, _width)
break;
case 'turnLeft':
model = this.change_turnLeft(model, _length, _width)
break;
case 'turnRight':
model = this.change_turnRight(model, _length, _width)
break;
default:
break;
}
models.push(model)
}
let handleData = {
width: _width,
length: _length,
models,
positionType:targetPosition,
offsetInfo: block.offsetInfo
}
/** 左转 或者 右转 长宽值 要互换 */
if (action == 'turnLeft' || action == 'turnRight') {
handleData = {
...handleData,
width: _length,
length: _width,
}
}
/** 更加行为 以及是否 翻转 转换 板件的偏移值 */
if (isTurn) {
// 翻面 左右封边互换 上下不变
block.offsetInfo = {
...block.offsetInfo,
top: block.offsetInfo.bottom,
bottom: block.offsetInfo.top
}
}
switch (action) {
case 'turnAround':
// 翻转
block.offsetInfo = {
top: block.offsetInfo.bottom,
bottom: block.offsetInfo.top,
left: block.offsetInfo.right,
right: block.offsetInfo.left
}
break;
case 'turnLeft':
block.offsetInfo = {
top: block.offsetInfo.right,
right: block.offsetInfo.bottom,
bottom: block.offsetInfo.left,
left: block.offsetInfo.top
}
break
case 'turnRight':
block.offsetInfo = {
top: block.offsetInfo.left,
left: block.offsetInfo.bottom,
bottom: block.offsetInfo.right,
right: block.offsetInfo.top
}
break
default:
break;
}
return handleData
}
/** 获取翻面 后的 放置方式 */
private getPositionAfterTurnFace(v: PositionType) {
let res = transitions_PositionTurnFace[v]
return res
}
/** 获取行为 左|右|翻转|不需要操作 */
private getDir(v1: PositionType, v2: PositionType) {
let flag: RotationAction = 'doNothing'
try {
flag = transitions_PositionToAction[v1][v2]
} catch (error) {
console.log('逻辑异常!请保证 v1 v2 的放置方式处在同一个面上,或者输入的值在转换关系中没有匹配的值')
flag = 'doNothing'
}
return flag
}
/** 右转 */
private change_turnRight(model: ModelProcessItem, valueX: number, valueY: number) {
let newModel = { ...model }
for (const i in newModel.pts) {
newModel.pts[i] = {
x: valueX - newModel.pts[i].y,
y: newModel.pts[i].x
}
}
return newModel
}
/** 左转 */
private change_turnLeft(model: ModelProcessItem, valueX: number, valueY: number) {
let newModel = { ...model }
for (const i in newModel.pts) {
newModel.pts[i] = {
x: newModel.pts[i].y,
y: valueX - newModel.pts[i].x
}
}
return newModel
}
/** 翻转 改变 xy 坐标 */
private change_turnAround(model: ModelProcessItem, valueX: number, valueY: number) {
let newModel = { ...model }
for (const i in newModel.pts) {
newModel.pts[i] = {
x: valueX - newModel.pts[i].x,
y: valueY - newModel.pts[i].y
}
}
return newModel
}
/**翻面 改变 y坐标 */
private change_turnFace(model: ModelProcessItem, valueY: number) {
let newModel = { ...model }
for (const i in newModel.pts) {
newModel.pts[i].y = valueY - newModel.pts[i].y
}
return newModel
}
/** 给 弧度值 取反 */
private reverseBuls(model: ModelProcessItem) {
let newModel = { ...model }
for (const i in newModel.buls) {
newModel.buls[i] = -newModel.buls[i]
}
return newModel
}
/**正面的放置方式 集合*/
private frontArr = [
PositionType.FRONT,
PositionType.FRONT_TURN_BACK,
PositionType.FRONT_TURN_LEFT,
PositionType.FRONT_TURN_RIGHT
]
/** 反面的放置方式 集合*/
private backArr = [
PositionType.BACK,
PositionType.BACK_TURN_BACK,
PositionType.BACK_TURN_LEFT,
PositionType.BACK_TURN_RIGHT
]
/** 根据放置方式 获取面 */
private getFace(positionValue: PositionType) {
let res = -1
if (this.frontArr.includes(positionValue)) {
res = FaceType.FRONT
}
if (this.backArr.includes(positionValue)) {
res = FaceType.BACK
}
return res
}
/** 根据 优化后的坐标XY便宜 */
handleByPlaceXY(models: ModelProcessItem[], placeX: number, placeY: number) {
let newModels: ModelProcessItem[] = []
for (const model of models) {
let newModel:ModelProcessItem = {...model}
let newPts = []
for (const k in newModel.pts) {
let p = {
x: model.pts[k].x + placeX,
y: model.pts[k].y + placeY
}
newPts.push(p)
}
newModel.pts = newPts
newModels.push(newModel)
}
return newModels
}
}
/** 处理器输入-- 获取造型在大板的刀路 */
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 interface IPoint { x: number, y: number; }
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'
/**
* 原放置方式 依据 目标放置方式 转 行为的 转换关系
*
* 注:原放置方式 为 翻面 转换后 的数值
* 若要支持 翻转转换前 需对内容进一步填充
*/
export const transitions_PositionToAction: any = {
[PositionType.FRONT]: {
[PositionType.FRONT_TURN_BACK]: 'turnAround',
[PositionType.FRONT_TURN_LEFT]: 'turnLeft',
[PositionType.FRONT_TURN_RIGHT]: 'turnRight',
[PositionType.FRONT]: 'doNothing'
},
[PositionType.FRONT_TURN_LEFT]: {
[PositionType.FRONT]: 'turnRight',
[PositionType.FRONT_TURN_RIGHT]: 'turnAround',
[PositionType.FRONT_TURN_BACK]: 'turnLeft',
[PositionType.FRONT_TURN_LEFT]: 'doNothing'
},
[PositionType.FRONT_TURN_RIGHT]: {
[PositionType.FRONT]: 'turnLeft',
[PositionType.FRONT_TURN_LEFT]: 'turnAround',
[PositionType.FRONT_TURN_BACK]: 'turnRight',
[PositionType.FRONT_TURN_RIGHT]: 'doNothing'
},
[PositionType.FRONT_TURN_BACK]: {
[PositionType.FRONT]: 'turnAround',
[PositionType.FRONT_TURN_LEFT]: 'turnRight',
[PositionType.FRONT_TURN_RIGHT]: 'turnLeft',
[PositionType.FRONT_TURN_BACK]: 'doNothing'
},
[PositionType.BACK]: {
[PositionType.BACK_TURN_BACK]: 'turnAround',
[PositionType.BACK_TURN_LEFT]: 'turnLeft',
[PositionType.BACK_TURN_RIGHT]: 'turnRight',
[PositionType.BACK]: 'doNothing'
},
[PositionType.BACK_TURN_LEFT]: {
[PositionType.BACK]: 'turnRight',
[PositionType.BACK_TURN_RIGHT]: 'turnAround',
[PositionType.BACK_TURN_BACK]: 'turnLeft',
[PositionType.BACK_TURN_LEFT]: 'doNothing'
},
[PositionType.BACK_TURN_RIGHT]: {
[PositionType.BACK]: 'turnLeft',
[PositionType.BACK_TURN_LEFT]: 'turnAround',
[PositionType.BACK_TURN_BACK]: 'turnRight',
[PositionType.BACK_TURN_RIGHT]: 'doNothing'
},
[PositionType.BACK_TURN_BACK]: {
[PositionType.BACK]: 'turnAround',
[PositionType.BACK_TURN_LEFT]: 'turnRight',
[PositionType.BACK_TURN_RIGHT]: 'turnLeft',
[PositionType.BACK_TURN_BACK]: 'doNothing'
}
}
/** 放置方式 翻转后的转换关系 */
export const transitions_PositionTurnFace: any = {
[PositionType.FRONT]: PositionType.BACK,
[PositionType.FRONT_TURN_RIGHT]: PositionType.BACK_TURN_LEFT,
[PositionType.FRONT_TURN_BACK]: PositionType.BACK_TURN_BACK,
[PositionType.FRONT_TURN_LEFT]: PositionType.BACK_TURN_RIGHT,
[PositionType.BACK]: PositionType.FRONT,
[PositionType.BACK_TURN_BACK]: PositionType.FRONT_TURN_BACK,
[PositionType.BACK_TURN_LEFT]: PositionType.FRONT_TURN_RIGHT,
[PositionType.BACK_TURN_RIGHT]: PositionType.FRONT_TURN_LEFT
}

View File

@@ -0,0 +1 @@
{"version":"3.2.4","results":[[":modelProcessPoints.test.ts",{"duration":7.6438000202178955,"failed":false}]]}

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />

39
tsconfig.app.json Normal file
View File

@@ -0,0 +1,39 @@
{
// TODO: Warn: 在进行TS类型检查的时候会检查到workflow工作区中的文件原因未知
"compilerOptions": {
"lib": [
"ES2020",
"ES2021",
"ESNext",
"DOM",
"DOM.Iterable"
],
"noEmit": true,
"target": "esnext",
"module": "ESNext",
"moduleResolution": "Bundler",
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
// 为了保证导入不与被引入项目冲突,不应该配置'@/*'别名
"@libs/*": [
"./src/*"
]
},
/* Linting */
"strict": true,
"erasableSyntaxOnly": false,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"composite": true
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
],
"exclude": [
"src/**/__tests__/*",
"dist",
"node_modules/**"
]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

25
tsconfig.node.json Normal file
View File

@@ -0,0 +1,25 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
},
"include": ["vite.config.ts"]
}

34
vite.config.ts Normal file
View File

@@ -0,0 +1,34 @@
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills';
import { resolve } from 'node:path';
import dts from 'vite-plugin-dts';
let basePath = process.env.basePath ?? '';
// https://vite.dev/config/
export default defineConfig({
base: basePath,
plugins: [
nodePolyfills(),
dts({rollupTypes: true, tsconfigPath: './tsconfig.app.json',insertTypesEntry: true}),
],
build: {
modulePreload: {
resolveDependencies() {
return [];
}
},
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'MesCutorder',
fileName(format) {
return `mes-cutorder.${format}.js`
},
formats: ['es', 'umd', 'iife']
}
},
esbuild: {
drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : [],
},
})