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); } }