feat:提交
This commit is contained in:
404
tests/dev1/dataHandle/common/LayoutEngine/RemainHelper.ts
Normal file
404
tests/dev1/dataHandle/common/LayoutEngine/RemainHelper.ts
Normal file
@@ -0,0 +1,404 @@
|
||||
import type { Polyline } from 'cadapi'
|
||||
import { Polyline2Points } from 'cadapi'
|
||||
import { Vector3 } from 'three'
|
||||
import { ArrayExt } from '../base/ArrayExt.js'
|
||||
import { Arc2d } from '../base/CAD.js'
|
||||
import { arrayRemoveDuplicateBySort } from '../ArrayExt.js'
|
||||
import { InitClipperCpp } from '../ClipperCpp.js'
|
||||
import type { Vector2 } from '../Vector2.js'
|
||||
import { Container } from '../core/Container.js'
|
||||
import { NestCache } from '../core/NestCache.js'
|
||||
import { ParseOddments } from '../core/ParseOddments.js'
|
||||
import { Part } from '../core/Part.js'
|
||||
import { Path } from '../core/Path.js'
|
||||
import { PathGeneratorSingle } from '../core/PathGenerator.js'
|
||||
import type { PlaceBlock,PlaceBoard,PlaceMaterial,BlockModel,RemainBlock} from '../../confClass.js'
|
||||
|
||||
import { BlockPlus } from './BlockPlus.js'
|
||||
import { BlockSizePlus } from './BlockSizePlus.js'
|
||||
import { BlockHelper } from './BlockHelper.js'
|
||||
// import { PlaceStore } from '../../vo/order/PlaceStore.js'
|
||||
import { PolylineHelper } from './PolylineHelper.js'
|
||||
import { CurveWrap, Points2Polyline } from './Curves2Parts.js'
|
||||
|
||||
/** 自动生成余料板空间 */
|
||||
export class RemainHelper {
|
||||
squarePath: Path
|
||||
canPutPaths: Path[]
|
||||
offset = 3.5
|
||||
|
||||
initSquarePath() {
|
||||
// let squareWidth = PlaceStore.sysConfig.scrapBlockSquare || 200;
|
||||
// let widthMin = PlaceStore.sysConfig.srcapBlockWidthMin || 100;
|
||||
// let widthMax = PlaceStore.sysConfig.scrapBlockWidthMax || 600;
|
||||
let squareWidth = 200
|
||||
let widthMin = 100
|
||||
let widthMax = 600
|
||||
this.squarePath = NestCache.CreatePath(60, 60, 0)
|
||||
this.canPutPaths = [
|
||||
NestCache.CreatePath(squareWidth, squareWidth, 0),
|
||||
NestCache.CreatePath(widthMin, widthMax, 0),
|
||||
NestCache.CreatePath(widthMax, widthMin, 0),
|
||||
]
|
||||
}
|
||||
|
||||
/** 分析所有余料空间 */
|
||||
async analyzeAllRemainSpace(pm1: PlaceMaterial, pb1: PlaceBoard, preMillingSize: number) {
|
||||
this.initSquarePath()
|
||||
console.log('分析所有余料空间');
|
||||
if (pm1 && pb1) // 当前板 先做
|
||||
{
|
||||
|
||||
this.offset = (pm1.diameter + pm1.cutKnifeGap) / 2 + preMillingSize
|
||||
pb1.isCreateRemainSpace = true
|
||||
await this.analyzeScrapSpace(pm1, pb1)
|
||||
}
|
||||
let order = PlaceStore.order
|
||||
for (let pm of order.materialList) {
|
||||
this.offset = (pm.diameter + pm.cutKnifeGap ) / 2 + preMillingSize
|
||||
for (let pb of pm.boardList) {
|
||||
await this.analyzeScrapSpace(pm, pb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 分析所有余料空间 */
|
||||
async analyzeScrapSpace(pm: PlaceMaterial, board: PlaceBoard) {
|
||||
if (board.isCreateRemainSpace == false)
|
||||
return
|
||||
await InitClipperCpp()
|
||||
|
||||
let cutBoardBorder = PlaceStore.sysConfig.cutBoardBorder;
|
||||
let cutBoardBorderB = PlaceStore.sysConfig.cutBoardBorderB
|
||||
if (board.isTwoFaceProcessing == false)
|
||||
cutBoardBorderB = 0
|
||||
let binPath: Path
|
||||
if (board.points && board.points.length > 0) // 非矩形板
|
||||
{
|
||||
binPath = new Path(board.points)
|
||||
}
|
||||
else // 矩形板
|
||||
{
|
||||
binPath = new Path([{ x: cutBoardBorder, y: cutBoardBorder },
|
||||
{ x: board.width - cutBoardBorderB, y: cutBoardBorder },
|
||||
{ x: board.width - cutBoardBorderB, y: board.length - cutBoardBorderB },
|
||||
{ x: cutBoardBorder, y: board.length - cutBoardBorderB },
|
||||
])
|
||||
}
|
||||
binPath.Id = undefined
|
||||
PathGeneratorSingle.RegisterId(binPath)
|
||||
// 容器
|
||||
let container = new Container(binPath)
|
||||
|
||||
for (let i = 0; i < board.blockList.length; i++) {
|
||||
let block = board.blockList[i]
|
||||
let part = this.toPart(block, binPath, this.offset * 2, i)
|
||||
// 设置位置
|
||||
part[0].PlacePosition = { x: (part[1].x + block.placeX + block.placeOffX - cutBoardBorder) * 1e4, y: (part[1].y + block.placeY + block.placeOffY - cutBoardBorder) * 1e4 }
|
||||
container.PlacedParts.push(part[0])
|
||||
}
|
||||
// for (let i = 0; i < board.BlockList.length; i++)
|
||||
// {
|
||||
// let block = board.BlockList[i];
|
||||
// let part = this.toPart(block, binPath, 0, board.BlockList.length + i);
|
||||
// //设置位置
|
||||
// part[0].PlacePosition = { x: (part[1].x + block.placeX - border) * 1e4, y: (part[1].y + block.placeY - border) * 1e4 };
|
||||
// container.PlacedParts.push(part[0]);
|
||||
// }
|
||||
|
||||
// let f = new NestFiler();
|
||||
// f.Write(container.PlacedParts.length);
|
||||
// for (let part of container.PlacedParts)
|
||||
// f.Write(part.State.Contour.BigIntPoints);
|
||||
// container.WriteFile(f);
|
||||
// console.log(JSON.stringify(f._datas));
|
||||
|
||||
// FileZip.WriteFile(`${board.boardId}.container`, JSON.stringify(f._datas));
|
||||
|
||||
board.remainBlockList = [] // 清空
|
||||
try {
|
||||
let spaces = ParseOddments(container, binPath, this.offset, this.squarePath, this.canPutPaths)
|
||||
for (let space of spaces) {
|
||||
let sb = this.toRemainBlock(space)
|
||||
sb.placeX = sb.placeX + cutBoardBorder
|
||||
sb.placeY = sb.placeY + cutBoardBorder
|
||||
board.remainBlockList.push(sb)
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
// throw new Error(`计算余料空间异常:${pm.FullName} 第${board.boardId}片`);
|
||||
console.log(`计算余料空间异常:${pm.fullName} 第${board.boardId}片`, err)
|
||||
throw new Error(`计算余料空间异常:${pm.fullName} 第${board.boardId}片;请保存优化联系售后工程师分析`)
|
||||
}
|
||||
|
||||
// 分析小板内 造型孔洞 余料空间
|
||||
for (let block of board.blockList) {
|
||||
let childBlocks: PlaceBlock[]
|
||||
// 造型孔
|
||||
for (let m of block.models) {
|
||||
if (m.depth < block.thickness - 0.05)
|
||||
continue
|
||||
if (m.hasContour == false)
|
||||
continue
|
||||
if (m.isCutting == false)
|
||||
continue
|
||||
if (!childBlocks) {
|
||||
childBlocks = board.blockList.filter(t => t.placeX > block.placeX
|
||||
&& t.placeX + t.placeWidth < block.placeX + block.placeWidth
|
||||
&& t.placeY > block.placeY
|
||||
&& t.placeY + t.placeLength < block.placeY + block.placeLength)
|
||||
}
|
||||
let spaces = await this.getModelSpace(block, m, childBlocks)
|
||||
for (let space of spaces) {
|
||||
let sb = this.toRemainBlock(space)
|
||||
// sb.placeX += border;
|
||||
// sb.placeY += border;
|
||||
sb.placeX += block.placeX
|
||||
sb.placeY += block.placeY
|
||||
board.remainBlockList.push(sb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
board.isCreateRemainSpace = false
|
||||
// console.log(`board id=${board.boardId} find scrap block count = ${board.remainBlockList.length}`);
|
||||
// console.log(`${board.boardId} scraping ok`)
|
||||
}
|
||||
|
||||
/** 获取造型空间 */
|
||||
async getModelSpace(block: PlaceBlock, m: BlockModel, blocks: PlaceBlock[]): Promise<any[]> {
|
||||
if (m.originModeling == null)
|
||||
return []
|
||||
let pts = m.originModeling.outline.pts
|
||||
let buls = m.originModeling.outline.buls
|
||||
|
||||
if (pts == undefined) {
|
||||
pts = m.originModeling.outline.map(e => e.pts)
|
||||
}
|
||||
|
||||
if (buls == undefined) {
|
||||
buls = m.originModeling.outline.map(e => e.buls)
|
||||
}
|
||||
// blocks = [];
|
||||
// 起点
|
||||
let pts_n = []
|
||||
for (let i = 0; i < pts.length; i++) {
|
||||
let p0 = BlockHelper.getPlaceXYInBlock(block, pts[i].x, pts[i].y, false, false)
|
||||
pts_n.push({ x: p0.x, y: p0.y, bul: buls[i] })
|
||||
}
|
||||
// 如果反面,翻转方向,重设坐标
|
||||
if (block.isTurnOver)
|
||||
pts_n = this.reversePoint(pts_n, block.cutWidth)
|
||||
|
||||
// pts_n.forEach(t => { t.x += block.placeX; t.y += block.placeY; });
|
||||
|
||||
let polyLine
|
||||
polyLine = Points2Polyline(pts_n)
|
||||
let cicle = PolylineHelper.ConverPolyLin2Circle(polyLine)
|
||||
if (cicle)
|
||||
polyLine = cicle
|
||||
let cureW2 = new CurveWrap(polyLine, this.offset, false)
|
||||
let pts2 = cureW2.GetInsidePoints2(this.offset)
|
||||
if (!pts2)
|
||||
return []
|
||||
let posx = 10000
|
||||
let posy = 10000
|
||||
for (let p of pts2) {
|
||||
if (p.x < posx)
|
||||
posx = p.x
|
||||
if (p.y < posy)
|
||||
posy = p.y
|
||||
}
|
||||
for (let p of pts2) {
|
||||
p.x -= posx
|
||||
p.y -= posy
|
||||
}
|
||||
let binPath = new Path(pts2)
|
||||
if (binPath.Area < 15000)
|
||||
return []
|
||||
|
||||
await InitClipperCpp()
|
||||
binPath.Id = undefined
|
||||
PathGeneratorSingle.RegisterId(binPath)
|
||||
// 容器
|
||||
let container = new Container(binPath)
|
||||
let i = 0
|
||||
for (let b of blocks) {
|
||||
let part = this.toPart(b, binPath, this.offset, i)
|
||||
if (part[0].State.Contour == null)
|
||||
continue
|
||||
part[0].Id = i++
|
||||
// let isin = this.blockInBlock(block, b, polyLine);
|
||||
// if (isin == false) continue;
|
||||
// let part = this.toPart(b, binPath, this.offset, i++);
|
||||
|
||||
// 设置位置
|
||||
part[0].PlacePosition = { x: (b.placeX - block.placeX - posx) * 1e4, y: (b.placeY - block.placeY - posy) * 1e4 }
|
||||
container.PlacedParts.push(part[0])
|
||||
}
|
||||
try {
|
||||
// let f = new NestFiler();
|
||||
// f.Write(container.PlacedParts.length);
|
||||
// for (let part of container.PlacedParts)
|
||||
// f.Write(part.State.Contour.BigIntPoints);
|
||||
// container.WriteFile(f);
|
||||
// console.log(JSON.stringify(f._datas));
|
||||
// FileZip.WriteFile(`${board.boardId}.container`, JSON.stringify(f._datas));
|
||||
|
||||
let spaces = ParseOddments(container, binPath, this.offset, this.squarePath, this.canPutPaths)
|
||||
for (let space of spaces) {
|
||||
space.Points.forEach((t) => { t.x += posx; t.y += posy })
|
||||
}
|
||||
return spaces
|
||||
}
|
||||
catch (err) {
|
||||
console.log(`板${block.blockNo}造型孔分析余料空间失败.`)
|
||||
throw new Error(`板${block.blockNo}造型孔分析余料空间失败.请保存优化联系售后工程师分析`)
|
||||
}
|
||||
// let spaces = ParseOddments(container, binPath, this.offset, this.squarePath, this.canPutPaths);
|
||||
// for (let space of spaces)
|
||||
// {
|
||||
// space.Points.forEach(t => { t.x += posx; t.y += posy; });
|
||||
// }
|
||||
// return spaces;
|
||||
}
|
||||
|
||||
/** 转配件 */
|
||||
toPart(block: PlaceBlock, binPath: Path, offset: number, id: number): [Part, Vector2] {
|
||||
let w = 10000
|
||||
let binPath2 = new Path([{ x: -w, y: -w }, { x: w, y: -w }, { x: w, y: w }, { x: -w, y: w }])
|
||||
let part = new Part()
|
||||
part.Id = id
|
||||
|
||||
part.UserData = { bno: block.blockNo, area: block.area, isUnRegular: block.isUnRegular, width: block.cutWidth, length: block.cutLength }
|
||||
|
||||
let path: Path
|
||||
let bPoint: Vector2
|
||||
|
||||
let sizeOff = BlockSizePlus.getOffDis(block) // 外扩偏移
|
||||
let hasSizeOff = (sizeOff.left + sizeOff.right + sizeOff.top + sizeOff.bottom) > (block.placeMaterial.diameter * 2 + block.placeMaterial.cutKnifeGap * 2 + 0.01)
|
||||
|
||||
// 矩形且带板外不偏移
|
||||
if (block.isUnRegular == false && hasSizeOff == false) {
|
||||
path = NestCache.CreatePath(block.placeWidth + sizeOff.left + sizeOff.right, block.placeLength + sizeOff.top + sizeOff.bottom, offset)
|
||||
bPoint = path.OrigionMinPoint
|
||||
// 轮廓
|
||||
part.Init2(path, binPath2, [0])
|
||||
}
|
||||
else // 异形
|
||||
{
|
||||
let pts = []
|
||||
|
||||
let cured = BlockPlus.getBorder_moving(block)
|
||||
for (let l of cured) {
|
||||
if (l instanceof Arc2d) {
|
||||
pts.push({ x: l.StartPoint.m_X, y: l.StartPoint.m_Y, bul: l.Bul })
|
||||
}
|
||||
else {
|
||||
pts.push({ x: l.StartPoint.m_X, y: l.StartPoint.m_Y, bul: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
let polyLine
|
||||
polyLine = Points2Polyline(pts)
|
||||
polyLine.CloseMark = true
|
||||
|
||||
let cureW = new CurveWrap(polyLine, offset, true)
|
||||
let pts2 = cureW.GetOutsidePoints()
|
||||
arrayRemoveDuplicateBySort(pts, (p1, p2) => (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) < 1e-2)
|
||||
path = new Path(pts2)
|
||||
|
||||
let cicle = PolylineHelper.ConverPolyLin2Circle(polyLine)
|
||||
if (!cicle) {
|
||||
let area = path.BoundingBox.area - path.Area
|
||||
if (area < 15000 && pts.length > 6)
|
||||
path = NestCache.CreatePath(block.placeWidth, block.placeLength, offset)
|
||||
}
|
||||
part.Init2(path, binPath2, [0])
|
||||
|
||||
// 不能放下,那么尝试不简化路径
|
||||
if (!path.IsRect && !cicle && part.RotatedStates.length == 0) {
|
||||
let pts2 = Polyline2Points(polyLine, true, offset)[1]
|
||||
arrayRemoveDuplicateBySort(pts2, (p1, p2) => (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) < 1e-2)
|
||||
path = new Path(pts2)
|
||||
part.Init2(path, binPath, [0])
|
||||
}
|
||||
// 轮廓
|
||||
bPoint = path.OrigionMinPoint
|
||||
}
|
||||
|
||||
if (hasSizeOff) {
|
||||
bPoint.x = bPoint.x - sizeOff.left
|
||||
bPoint.y = bPoint.y - sizeOff.bottom
|
||||
}
|
||||
|
||||
return [part, bPoint]
|
||||
}
|
||||
|
||||
/** 余料板空间 */
|
||||
toRemainBlock(path: Path): RemainBlock {
|
||||
let bx = path.OrigionMinPoint.x
|
||||
let by = path.OrigionMinPoint.y
|
||||
let points = path.Points
|
||||
let ptsX = points.map((t) => { return t.x })
|
||||
let ptsY = points.map((t) => { return t.y })
|
||||
|
||||
let x0 = ArrayExt.min(ptsX, t => t)
|
||||
let y0 = ArrayExt.min(ptsY, t => t)
|
||||
let x1 = ArrayExt.max(ptsX, t => t)
|
||||
let y1 = ArrayExt.max(ptsY, t => t)
|
||||
|
||||
let sp = new RemainBlock(x0, y0, x1 - x0, y1 - y0)
|
||||
|
||||
let pts = points.map((t) => { return { x: t.x - x0, y: t.y - y0 } })
|
||||
|
||||
sp.placeX = bx + x0
|
||||
sp.placeY = by + y0
|
||||
|
||||
sp.setPoints(pts)
|
||||
return sp
|
||||
}
|
||||
|
||||
/** 判断板是否在造型洞里头 */
|
||||
blockInModelOfBlock(block: PlaceBlock, b: PlaceBlock, modlPl: Polyline, posX = 0, posY = 0): boolean {
|
||||
let cured = BlockPlus.getBorder(b)
|
||||
let pts = []
|
||||
for (let l of cured) {
|
||||
if (l instanceof Arc2d) {
|
||||
pts.push({ x: l.StartPoint.m_X, y: l.StartPoint.m_Y, bul: l.Bul })
|
||||
}
|
||||
else {
|
||||
pts.push({ x: l.StartPoint.m_X, y: l.StartPoint.m_Y, bul: 0 })
|
||||
}
|
||||
}
|
||||
let polyLine = Points2Polyline(pts)
|
||||
let cureW2 = new CurveWrap(polyLine, this.offset, false)
|
||||
let pts2 = cureW2.GetInsidePoints2(this.offset)
|
||||
|
||||
// pts2 = pts2.map(t => { return { x: t.x + b.placeX - block.placeX - posX, y: t.y + b.placeY - block.placeY - posY }; });
|
||||
|
||||
for (let t of pts2) {
|
||||
let x = t.x + b.placeX - block.placeX - posX
|
||||
let y = t.y + b.placeY - block.placeY - posY
|
||||
if (modlPl.PtInCurve(new Vector3(x, y, 0)) == false)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/** 反向点 */
|
||||
private reversePoint(pts, w): any[] {
|
||||
let newPts = []
|
||||
for (let i = pts.length - 1; i >= 0; i--) {
|
||||
let p = pts[i]
|
||||
let x = p.x
|
||||
let y = p.y
|
||||
let j = i == 0 ? pts.length - 1 : i - 1
|
||||
|
||||
let bul = pts[j].bul
|
||||
newPts.push({ x, y, bul })
|
||||
}
|
||||
return newPts
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user