Files
cut-abstractions/tests/dev1/dataHandle/common/cutorder/CutOrder.ts
2025-07-22 18:22:31 +08:00

388 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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