Files
cut-abstractions/tests/dev1/dataHandle/common/cutorder/CutOrder.ts

388 lines
14 KiB
TypeScript
Raw Normal View History

2025-07-22 18:22:31 +08:00
import { Point2d } from '../../common/base/CAD.js';
import { ArrayExt } from '../../common/base/ArrayExt.js';
import { BlockRegion, PlaceBlock, PlaceBoard, PlaceMaterial } from '../../confClass.js';
import { Point } from '../Vector2.js';
import { BlockPlus } from '../LayoutEngine/BlockPlus.js';
import {
KLSC, YH_bang
} from '../../confClass.js';
import { PlacePosition } from '../PlacePosition.js';
import { BlockHelper } from '../LayoutEngine/BlockHelper.js';
/** 开料顺序类 */
export class CutOrder {
/** 开料顺序 */
static autoSetCutOrder(materialList: PlaceMaterial[], config, useNewSort = false) {
// let order = PlaceStore.order;
// console.log(order.materialList);
let newMaterialList = [...materialList]
for (let pm of newMaterialList) {
for (let pb of pm.boardList) {
this.autoCalcCutOrder(pm, pb, config, useNewSort);
}
}
return newMaterialList
}
/** 自动计算开料顺序 useNewSort是否使用新开料顺序算法*/
static autoCalcCutOrder(pm: PlaceMaterial, pb: PlaceBoard, config, useNewSort = false) {
if (pb.isLocked) return;
let blocks = pb.blockList;
if (blocks.length == 0) return;
if (blocks.length == 1) {
blocks[0].cutOrder = 1;
}
else {
let isUseNewSort = useNewSort; //使用新开料顺序算法
if (isUseNewSort && checkBoardCross()) isUseNewSort = false; //异形穿插的大板使用旧的开料顺序算法
if (isUseNewSort) {
this.autoSortBlockNew(pm, pb);
}
else {
this.autoSortBlock(pm, pb, config);
}
}
console.log('开料顺序', pm, pb, pb.blockList.map(e => e.cutOrder));
// //自动设置下刀点
// CutPointHelper.autoFindCutPoint(pm, pb);
/** 判断大板是否有交叉的大板(包括、包含) */
function checkBoardCross() {
for (let i = 0; i < pb.blockList.length; i++) {
for (let j = i + 1; j < pb.blockList.length; j++) {
if (checkCross(pb.blockList[i], pb.blockList[j])) return true;
}
}
return false;
}
function checkCross(b1: PlaceBlock, b2: PlaceBlock): boolean {
let c1 = { x: b1.placeX + b1.placeWidth / 2, y: b1.placeY + b1.placeLength / 2 };
let c2 = { x: b2.placeX + b2.placeWidth / 2, y: b2.placeY + b2.placeLength / 2 };
return Math.abs(c1.x - c2.x) < (b1.placeWidth / 2 + b2.placeWidth / 2) && Math.abs(c1.y - c2.y) < (b1.placeLength / 2 + b2.placeLength / 2);
}
return pb
}
/** 新开料顺序算法 */
static autoSortBlockNew(pm: PlaceMaterial, pb: PlaceBoard) {
let selectBs = pb.blockList;
let beginId = 0;
const has0 = pb.blockList.filter(t => t.cutOrder == 0);
const has1 = pb.blockList.filter(t => t.cutOrder > 0);
const has2 = pb.blockList.filter(t => t.cutOrder < 0);
//有手动指定开料顺序的
if (has0.length > 0 && (has1.length + has2.length) > 0) {
selectBs = has0;
if (has1.length > 0) //开头的
{
const bs = has1.sort((a, b) => a.cutOrder - b.cutOrder);
for (const b of bs) {
beginId++;
b.cutOrder = beginId;
}
}
if (has2.length > 0) //结尾的
{
const bs = has2.sort((a, b) => a.cutOrder - b.cutOrder);
let endId = has0.length + has1.length;
for (const b of bs) {
endId++;
b.cutOrder = endId;
}
}
}
let bangs: YH_bang[] = [];
let blocks = new Array();
for (let i = 0; i < selectBs.length; i++) {
let block = selectBs[i];
let bangid = i + 1;
let line = 0;
let x = block.placeX;
let y = block.placeY;
let pbg = block.placeLength;
let pbk = block.placeWidth;
let ishb = false; //是否参与合并的板
let isbig = false; //是否为合并的大板;
blocks[bangid] = block;
bangs.push({
bangid,
line: 0,
pbg,
pbk,
x,
y,
ishb: false,
hb: [],
isbig: false,
isqg: false,
isgr: false,
gr: [],
grid: -1
});
}
let dt = pm.diameter + pm.cutKnifeGap;
let k = pb.width;
let g = pb.length;
let xdsc = new KLSC(bangs, k, g, dt, 0, 0, 1);
let rt = xdsc.SCid;
// let rt = JSXDSC(bangs, dt, k, g);
if (rt.length < selectBs.length) return;
for (let i = 0; i < rt.length; i++) {
let bid = rt[i];
beginId++;
blocks[bid].cutOrder = beginId;
}
}
/** 设置下刀点 */
static setCutPoint(block: PlaceBlock, cutRegion: BlockRegion) {
block.cutRegion = cutRegion;
let x = 0,
y = 0;
if (cutRegion == BlockRegion.RIGHT_BOTTOM)
// if (cutRegion == BlockRegion.LEFT_BOTTOM)
{
x = block.placeWidth;
}
if (cutRegion == BlockRegion.RIGHT_TOP)
// if (cutRegion == BlockRegion.RIGHT_BOTTOM)
{
x = block.placeWidth;
y = block.placeLength;
}
if (cutRegion == BlockRegion.LEFT_TOP)
// if (cutRegion == BlockRegion.RIGHT_TOP)
{
y = block.placeLength;
}
block.cutPointId = CutOrder.findCutPoint(block, x, y);
}
/**计算下刀点 */
private static findCutPoint(block: PlaceBlock, x: number, y: number): number {
let list = BlockPlus.getBorder(block);
let p = new Point2d(x, y);
let mV = Number.MAX_VALUE;
let index = 0;
for (let i = 0; i < list.length; i++) {
let v = p.DistensTo(list[i].StartPoint);
if (v < mV) {
mV = v;
index = i;
}
}
return index;
}
/**获得下刀点坐标 */
static getCutPointInBloard(block: PlaceBlock): Point {
let curves = BlockPlus.getBorder(block);
while (block.cutPointId >= curves.length) {
block.cutPointId = block.cutPointId - curves.length;
}
while (block.cutPointId < 0) {
block.cutPointId = block.cutPointId + curves.length;
}
let p = curves[block.cutPointId].StartPoint;
//内收 一个刀直径
let rx = p.m_X;
let ry = p.m_Y;
let offv =
block.blockDetail.offsetKnifeRadius == 0 ? 6 : block.blockDetail.offsetKnifeRadius * 2;
if (p.m_X == 0) rx = offv;
if (p.m_X == block.placeWidth) rx = block.placeWidth - offv;
if (p.m_Y == 0) ry = offv;
if (p.m_Y == block.placeLength) ry = block.placeLength - offv;
return new Point(block.placeX + rx, block.placeY + ry);
}
//原开料顺序算法--------------------------------------------------------
//原开料顺序算法
static autoSortBlock(pm: PlaceMaterial, pb: PlaceBoard, config: any) {
let blocks = pb.blockList;
//将小板位置恢复到左下角靠板的状态
PlacePosition.resetPlacePosition(pb, config);
//清除 开料顺序
//如果全部都有开料序号,(重新优化,或移动小板等需要清除开料顺序)
//手动设置开料顺序一半时,不能把设置的开料顺序清空
if (!blocks.some(t => t.cutOrder == 0)) {
blocks.forEach(t => (t.cutOrder = 0));
}
/**板板之间距离 */
let cutGap = pm.diameter + pm.cutKnifeGap;
let sortId = ArrayExt.max(blocks, t => t.cutOrder) + 1;
//优先排大板内部的小板
for (let block of blocks) {
if (block.cutOrder != 0) continue;
let blocks_innner = BlockHelper.getInnerBlock(block, blocks);
if (blocks_innner && blocks_innner.length > 0) {
while (true) {
let topB = this.findTopBlock(blocks_innner);
if (topB == null) break;
let coverBlock = CutOrder.findCoverBlock(blocks_innner, topB);
coverBlock.cutOrder = sortId++;
}
}
}
//优先排四周(50mm)的小板
//优先排左侧小板
this.autoSortBlockLeft(blocks, cutGap);
//优先排下侧小板
this.autoSortBlockBottom(blocks, cutGap);
sortId = ArrayExt.max(blocks, t => t.cutOrder, t => true, 0) + 1;
while (true) {
let topB = this.findTopBlock(blocks);
if (topB == null) break;
let coverBlock = CutOrder.findCoverBlock(blocks, topB);
coverBlock.cutOrder = sortId++;
CutOrder.setCutPoint(coverBlock, BlockRegion.LEFT_BOTTOM);
}
if (blocks.some(t => t.cutOrder < 0)) //有手工设置倒数开料的
{
//将开料顺序为负数的设置成最后
for (let b of blocks) {
if (b.cutOrder < 0) b.cutOrder = b.cutOrder + blocks.length + 1;
}
}
else //全自动 开料顺序,比较最后2片,倒数第二片是否更优
{
let lastBlock = blocks.find(t => t.cutOrder == blocks.length);
let lastBlock2 = blocks.find(t => t.cutOrder == blocks.length - 1);
if (lastBlock && lastBlock2) {
if (lastBlock.area < 0.3 && lastBlock2.area - 0.1 > lastBlock.area) {
lastBlock.cutOrder--;
lastBlock2.cutOrder++;
}
}
}
//将小板位置 恢复到 系统配置的靠板 状态
PlacePosition.resetPlacePosition(pb, config);
}
/**手工排序:如果 cutOrder == 0 按当前板顺序,如果 >0 则设置为值板上比他的大的设置为0 */
static resetCutOrder(pb: PlaceBoard, block: PlaceBlock, cutOrder: number = 0) {
if (cutOrder > 0) {
block.cutOrder = cutOrder;
}
else {
for (let block of pb.blockList) {
if (block.cutOrder >= cutOrder) {
block.cutOrder = 0;
}
}
block.cutOrder = cutOrder;
}
}
/**优先排大板左侧的小板 (细长条) */
private static autoSortBlockLeft(blocks: PlaceBlock[], curtGap: number) {
// let sysConfig = PlaceStore.sysConfig;
let blockList = ArrayExt.orderBy(blocks, t => t.placeX, t => -t.placeY);
for (const b of blockList) {
// if (b.cutOrder != 0 || b.placeWidth > SysConfig.AutoSortingMinWidth) continue;
if (b.cutOrder != 0) continue;
//查询挡住的板个数 : 在b左边,且b的前 板件之内
let count = ArrayExt.count(
blockList,
t =>
t.cutOrder == 0 &&
b.placeX > t.placeX + t.placeWidth &&
t.placeY < b.placeY + b.placeLength &&
t.placeY + t.placeLength > b.placeY
);
if (count == 0) //判断是否在其他板内部
{
let count2 = ArrayExt.count(
blockList,
t =>
t.cutOrder == 0 &&
b.placeX >= t.placeX && b.placeX < t.placeX + t.placeWidth &&
t.placeY > b.placeY && t.placeY < b.placeY + b.placeLength
);
count += count2;
}
if (count == 0) {
b.cutOrder = ArrayExt.max(blockList, t => t.cutOrder, t => true, 0) + 1;
CutOrder.setCutPoint(b, BlockRegion.RIGHT_TOP);
}
}
}
/**优先排大板下侧的小板 (短粗条) */
private static autoSortBlockBottom(blocks: PlaceBlock[], curtGap: number) {
// let SysConfig = PlaceStore.sysConfig;
//排序,
let list = ArrayExt.orderBy(blocks, t => t.placeY, t => -t.placeX);
for (const b of list) {
// if (b.cutOrder != 0 || b.placeLength > SysConfig.AutoSortingMinWidth) continue;
if (b.cutOrder != 0) continue;
//查询挡住的板个数 : 在b上面边,且b的前 板件之内
let count = ArrayExt.count(list, t =>
t.cutOrder == 0 &&
b.placeY > t.placeY + t.placeLength &&
t.placeX < b.placeX + b.placeWidth &&
t.placeX + t.placeWidth > b.placeX);
if (count == 0) {
b.cutOrder = ArrayExt.max(list, t => t.cutOrder, t => true, 0) + 1;
CutOrder.setCutPoint(b, BlockRegion.LEFT_TOP);
}
}
}
//block列表中找到最远的板
static findTopBlock(blocks: PlaceBlock[]): PlaceBlock {
return ArrayExt.last(blocks, t => t.cutOrder == 0, t => t.placeY + t.placeLength, t => t.placeX + t.placeWidth);
}
//寻找挡在block之前(x+)的板
private static findCoverBlock(blocks: PlaceBlock[], block: PlaceBlock): PlaceBlock {
let unSortCount = ArrayExt.count(blocks, t => t.cutOrder == 0);
if (unSortCount == 1) return block;
let blocks_cover: PlaceBlock[] = [];
//可以挡住block 的板列表: 在block 右边(posx 大) ,顶点在 block 之间
blocks_cover = ArrayExt.where(blocks, t =>
t.cutOrder == 0
&& t != block
&& t.placeX > block.placeX
&& t.placeY + t.placeLength > block.placeY);
//如果没有挡住的板,则返回最高板
if (blocks_cover.length == 0) return block;
let nextBlock = ArrayExt.last(blocks_cover, t => true, t => t.placeY + t.placeLength);
return CutOrder.findCoverBlock(blocks, nextBlock);
}
}