feat:提交
This commit is contained in:
1713
tests/dev1/dataHandle/common/LayoutEngine/BlockDataPlus.ts
Normal file
1713
tests/dev1/dataHandle/common/LayoutEngine/BlockDataPlus.ts
Normal file
File diff suppressed because it is too large
Load Diff
365
tests/dev1/dataHandle/common/LayoutEngine/BlockDoFace.ts
Normal file
365
tests/dev1/dataHandle/common/LayoutEngine/BlockDoFace.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
|
||||
import { BlockHelper } from './BlockHelper.js'
|
||||
import { BlockSizePlus } from './BlockSizePlus.js'
|
||||
import { resetPlaceBoard } from './PlaceBase.js'
|
||||
import { NcCustomized } from '@/imes/biz/NcCustomized.js'
|
||||
import { FaceType, HoleType, HoleArrange, PlaceBlock, PlaceMaterial, PlaceBoard } from '../../confClass.js'
|
||||
|
||||
/** 计算大板中所有的小板的正反面孔造型数量,让正面加工时间接近 比例 30% */
|
||||
export async function TurnOverWithDoneRate(pm: PlaceMaterial, cncData: boolean, doneRate: number, sp_h: number, sp_m: number) {
|
||||
let i = 0
|
||||
console.time(`${pm.fullName} full use time`)
|
||||
for (let pb of pm.boardList) {
|
||||
// if (pb.IsOddmengt) continue;
|
||||
i++
|
||||
// console.log(`${pm.FullName} bid=${i} 开始计算`);
|
||||
await TurnOverWithDoneRate_pb(pm, pb, cncData, doneRate, sp_h, sp_m, i)
|
||||
}
|
||||
console.timeEnd(`${pm.fullName} full use time`)
|
||||
}
|
||||
|
||||
/** 计算大板中所有的小板的正反面孔造型数量,让正面加工时间接近 比例 30% */
|
||||
export async function TurnOverWithDoneRate_pb(pm: PlaceMaterial, pb: PlaceBoard, useCncData: boolean, doneRate: number, sp_h: number, sp2: number, i) {
|
||||
// 孔 0.5秒/个
|
||||
// 造型, 16秒/米 , 0.016秒/mm
|
||||
|
||||
let sp_m = sp2 * 0.001
|
||||
let rate = doneRate * 0.01
|
||||
|
||||
// 一. 先求出大板所有小板,当前正反面加工时间,是否可翻转
|
||||
let tm: any = [] //
|
||||
|
||||
let t1_z = 0 // 不可翻 正面加工时间 合计
|
||||
let t1_f = 0 // 不可翻 反面加工时间 合计
|
||||
let t2_z = 0 // 可翻 正面加工时间 合计
|
||||
let t2_f = 0 // 可翻 反面加工时间 合计
|
||||
|
||||
let t_full = 0 // 所有加工时间 合计
|
||||
let t_throngh = 0// 挖穿的造型的时间,
|
||||
|
||||
for (let block of pb.blockList) {
|
||||
let tc = 0 // 挖穿;
|
||||
let tz = 0 // 当前加工面 时间
|
||||
let tf = 0 // 反面 时间
|
||||
|
||||
let holes = block.holeListFaceA
|
||||
let models = block.modelListFaceA
|
||||
if (useCncData) // 使用cnc 数据
|
||||
{
|
||||
holes = block.isTurnOver ? block.holeListOrgFaceB.filter(t => t.isCutting == false) : block.holeListOrgFaceA.filter(t => t.isCutting == false)
|
||||
models = block.isTurnOver ? block.modelListOrgFaceB.filter(t => t.isCutting == false) : block.modelListOrgFaceA.filter(t => t.isCutting == false)
|
||||
}
|
||||
tz += holes.length * sp_h // 正面孔时间
|
||||
for (let model of models) {
|
||||
let len = 0
|
||||
for (let i = 0; i < model.pointList.length - 1; i++) {
|
||||
let p0 = model.pointList[i]
|
||||
let p1 = model.pointList[i + 1]
|
||||
if (p0 == null || p1 == null)
|
||||
continue
|
||||
len += Math.sqrt((p0.pointX - p1.pointX) ** 2 + (p0.pointY - p1.pointY) ** 2)
|
||||
}
|
||||
if (model.depth >= block.thickness - 0.001) {
|
||||
tc += len * sp_m
|
||||
}
|
||||
else {
|
||||
tz += len * sp_m
|
||||
}
|
||||
}
|
||||
|
||||
let holes2 = block.holeListFaceB
|
||||
let models2 = block.modelListFaceB
|
||||
if (useCncData) // 使用cnc 数据
|
||||
{
|
||||
holes2 = block.isTurnOver ? block.holeListOrgFaceA.filter(t => t.isCutting == false) : block.holeListOrgFaceB.filter(t => t.isCutting == false)
|
||||
models2 = block.isTurnOver ? block.modelListOrgFaceB.filter(t => t.isCutting == false) : block.modelListOrgFaceB.filter(t => t.isCutting == false)
|
||||
}
|
||||
tf += holes2.length * sp_h
|
||||
for (let model of models2) {
|
||||
let len = 0
|
||||
for (let i = 0; i < model.pointList.length - 1; i++) {
|
||||
let p0 = model.pointList[i]
|
||||
let p1 = model.pointList[i + 1]
|
||||
if (p0 == null || p1 == null)
|
||||
continue
|
||||
len += Math.sqrt((p0.pointX - p1.pointX) ** 2 + (p0.pointY - p1.pointY) ** 2)
|
||||
}
|
||||
tf += len * sp_m
|
||||
}
|
||||
|
||||
if (tz + tc == 0 && tf == 0)
|
||||
continue // 没有加工
|
||||
|
||||
let canTurn = true
|
||||
if (block.isUnRegular)
|
||||
canTurn = false // 异形不能翻
|
||||
if (canTurn && block.placeHole != 2)
|
||||
canTurn = false // 非可翻转的
|
||||
if (canTurn && hasChildBlock(block, pb.blockList))
|
||||
canTurn = false// 里面有小板,不能翻
|
||||
|
||||
t_throngh += tc
|
||||
t_full += (tc + tz + tf)
|
||||
|
||||
if (canTurn == false) // 不能翻
|
||||
{
|
||||
t1_z += tz
|
||||
t1_f += tf
|
||||
}
|
||||
else // 可以翻 ,
|
||||
{
|
||||
if (Math.abs(tz - tf) > sp_h) // 翻面效果很差, 时间小于一个孔 ,翻不翻无所谓,
|
||||
{
|
||||
tm.push({ bno: block.blockNo, z: tz, f: tf, dis: tz - tf, child: [] })
|
||||
}
|
||||
t2_z += tz
|
||||
t2_f += tf
|
||||
}
|
||||
}
|
||||
|
||||
// 二. 计算 不可翻的小板 正面加工 与理论值 相差多少
|
||||
let t2_L = t_full * rate - t1_z - t_throngh // 剩下加工正面的时间 理论值 ( 理论值- 当前正面加工时间,就是剩下可翻的板 正面加工时间 )
|
||||
let tz: any = t1_z + t_throngh + t2_z // 当前正面时间
|
||||
let t2_dis = t2_z - t2_L // 理论值与 当前剩下正面加工时间的差值 ,应该要节省的时间总
|
||||
|
||||
// 正面加工时间 比例 接近预定于的比例 。 则不再计算
|
||||
if (Math.abs(100 * t2_dis / t_full) < 3)
|
||||
return { tz, fz: t_full - tz, full: t_full }
|
||||
|
||||
// 三.接下来的问题就是在 tm 中 找出 翻面加工省时 合计 最接近 t2_dis 的集合出来.
|
||||
let best_list: any = [] // 最优解 需要翻面的
|
||||
let best_v = t2_dis // 差值最大 默认当前
|
||||
|
||||
// 四.将tm 转换成 最多16个可翻面 的数组
|
||||
tm.sort((a, b) => a.dis - b.dis) // 按小到大排序
|
||||
|
||||
// 精简 tm //将tm中 翻面效果 相反的 且大的 剔除掉
|
||||
if (tm.length > 16) {
|
||||
if (t2_dis > 0) {
|
||||
for (let i = tm.length - 1; i >= 0; i--) {
|
||||
if (tm[i].dis < -t2_dis) {
|
||||
tm.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = tm.length - 1; i >= 0; i--) {
|
||||
if (tm[i].dis > -t2_dis) {
|
||||
tm.splice(i, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tm.length == 0)
|
||||
return { tz, fz: t_full - tz, full: t_full }
|
||||
|
||||
let blocks: any = []
|
||||
for (let b of pb.blockList) {
|
||||
blocks[b.blockNo] = b
|
||||
}
|
||||
|
||||
// 可翻转小板集合,精简
|
||||
tm = smallerBlocks(tm)
|
||||
|
||||
let rt = await getBestTurnBlocks(pm, i, tm, best_v, t_full)
|
||||
best_list = rt.result
|
||||
best_v = rt.v
|
||||
|
||||
// 四. 将最优解中的小板 翻面
|
||||
|
||||
for (let m of best_list) {
|
||||
tz -= m.dis
|
||||
let bs: any = []
|
||||
|
||||
let block: any = blocks[m.bno]
|
||||
bs.push(block)
|
||||
for (let no of m.child) {
|
||||
bs.push(blocks[no])
|
||||
}
|
||||
for (let block of bs) {
|
||||
let orgStyle = block.placeStyle
|
||||
let newStyle = BlockHelper.getTurnedPlaceStyle(orgStyle, 0)
|
||||
|
||||
let orgPlaceX = block.placeX - block.placeOffX
|
||||
let orgPlaceY = block.placeY - block.placeOffY
|
||||
|
||||
let offset = BlockSizePlus.getOffDis(block, newStyle)
|
||||
let newPlaceX = orgPlaceX + offset.x
|
||||
let newPlaceY = orgPlaceY + offset.y
|
||||
|
||||
block.placeX = newPlaceX
|
||||
block.placeY = newPlaceY
|
||||
block.placeOffX = offset.x
|
||||
block.placeOffY = offset.y
|
||||
// 修改小板的放置方式
|
||||
BlockHelper.resetPlaceStyle(block, newStyle)
|
||||
}
|
||||
}
|
||||
|
||||
// 重设pb
|
||||
if (best_list.length > 0)
|
||||
resetPlaceBoard(pm, pb)
|
||||
|
||||
return { tz, fz: t_full - tz, full: t_full }
|
||||
}
|
||||
|
||||
|
||||
/** 获取加工面:processMode: 0开料机加工 1开料机CNC组合加工 2定制加工 */
|
||||
export function getDoFace(block: PlaceBlock, processMode: number = 0): boolean {
|
||||
// 模式0: 开料机处理排钻, 造型, 采用大孔面作为正面的模式. 正面多加工, cnc多加工
|
||||
if (processMode == 0) {
|
||||
// 先考虑 设计开料面
|
||||
if (block.placeHole == HoleArrange.FRONT)
|
||||
return true
|
||||
if (block.placeHole == HoleArrange.BACK)
|
||||
return false
|
||||
// 造型单面 作为开料面
|
||||
if (block.modelCountFront() > 0 && block.modelCountBack() == 0)
|
||||
return true
|
||||
if (block.modelCountFront() == 0 && block.modelCountBack() > 0)
|
||||
return false
|
||||
|
||||
// 优先考虑 大孔面 多孔面
|
||||
return getHoleFaceMore(block)
|
||||
}
|
||||
|
||||
// 造型单面 作为开料面
|
||||
if (block.modelCountFront() > 0 && block.modelCountBack() == 0)
|
||||
return true
|
||||
if (block.modelCountFront() == 0 && block.modelCountBack() > 0)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
/** 大孔/孔多作为正面 */
|
||||
function getHoleFaceMore(block: PlaceBlock): boolean {
|
||||
if (!block.blockDetail) {
|
||||
console.log('getHoleFaceMore: blockDetail is undefind', block)
|
||||
return true
|
||||
}
|
||||
// 优先考虑 大孔面
|
||||
let bigHole = block.blockDetail.holes.find(t => t.holeType == HoleType.BIG_HOLE)
|
||||
if (bigHole)
|
||||
return bigHole.face == FaceType.FRONT
|
||||
// 多孔面
|
||||
if (block.blockDetail.holeListFaceA.length > block.blockDetail.holeListFaceB.length)
|
||||
return true
|
||||
if (block.blockDetail.holeListFaceA.length < block.blockDetail.holeListFaceB.length)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
// 大孔/孔多 作为正面
|
||||
function getHoleFace_more(block: PlaceBlock): boolean {
|
||||
// 优先考虑 大孔面
|
||||
let bigHole = block.holes().find(t => t.holeType == HoleType.BIG_HOLE)
|
||||
if (bigHole)
|
||||
return bigHole.face == FaceType.FRONT
|
||||
// 多孔面
|
||||
if (block.holeCountFront > block.holeCountBack)
|
||||
return true
|
||||
if (block.holeCountFront < block.holeCountBack)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
// 非大孔/孔少 作为正面
|
||||
function getHoleFace_less(block: PlaceBlock): boolean {
|
||||
// 优先考虑 大孔面
|
||||
let bigHole = block.holes().find(t => t.holeType == HoleType.BIG_HOLE)
|
||||
if (bigHole)
|
||||
return bigHole.face != FaceType.FRONT
|
||||
// 少孔面
|
||||
if (block.holeCountFront > block.holeCountBack)
|
||||
return false
|
||||
if (block.holeCountFront < block.holeCountBack)
|
||||
return true
|
||||
return true
|
||||
}
|
||||
|
||||
// 可翻转小板集合,精简
|
||||
function smallerBlocks(tm: any[]): any[] {
|
||||
if (tm.length <= 30)
|
||||
return tm
|
||||
let time = 1
|
||||
|
||||
if (tm.length > 40)
|
||||
time = 2
|
||||
if (tm.length > 60)
|
||||
time = 3
|
||||
if (tm.length > 80)
|
||||
time = 4
|
||||
if (tm.length > 100)
|
||||
time = 5
|
||||
if (tm.length > 120)
|
||||
time = 10
|
||||
|
||||
let newtm: any = []
|
||||
let newLength = Math.ceil(tm.length / time)
|
||||
for (let t = 0; t < newLength; t++) {
|
||||
let block0 = tm[t * time]
|
||||
block0.child = [] // bno: block.BlockNo, z: t1, f: t2, dis: t1 - t2
|
||||
for (let i = 1; i < time; i++) {
|
||||
let j = t * time + i
|
||||
if (j >= tm.length)
|
||||
break
|
||||
let blockNext = tm[j]
|
||||
block0.child.push(blockNext.bno)
|
||||
block0.z += blockNext.z
|
||||
block0.f += blockNext.f
|
||||
block0.dis += blockNext.dis
|
||||
}
|
||||
newtm.push(block0)
|
||||
}
|
||||
return newtm
|
||||
}
|
||||
|
||||
async function getBestTurnBlocks(pm, pid, tm: any[], best_v, t_full) {
|
||||
let result = []
|
||||
let result_v = best_v
|
||||
let disRate = 111111
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
let num1 = 1
|
||||
let num2 = 2 ** tm.length - 1
|
||||
|
||||
let isOK = false
|
||||
|
||||
// const worker = new Worker(new URL('./FaceDoTimeWorker', import.meta.url))
|
||||
// worker.onmessage = (res) =>
|
||||
// {
|
||||
// let tmp = res.data.tmp
|
||||
// let v = res.data.v
|
||||
// let rate = res.data.rate
|
||||
// result = tmp
|
||||
// result_v = v
|
||||
// disRate = rate
|
||||
// if (res.data.ok == 1)
|
||||
// {
|
||||
// worker.terminate() // 关闭
|
||||
// resolve({ result, v: result_v })
|
||||
// isOK = true
|
||||
// }
|
||||
// }
|
||||
// worker.postMessage({ num1, num2, blocks: tm, disTime: best_v, fullTime: t_full })
|
||||
// Sleep(3000).then(() =>
|
||||
// {
|
||||
// if (isOK)
|
||||
// return
|
||||
// resolve({ result, v: result_v })
|
||||
// // console.log(`${pm.FullName} bid=${pid} ${tm.length} 超时3秒`);
|
||||
// })
|
||||
})
|
||||
}
|
||||
|
||||
/** 判断block 里头有没有小板 */
|
||||
function hasChildBlock(block: PlaceBlock, blocks: PlaceBlock[]): boolean {
|
||||
for (let b of blocks) {
|
||||
if (b.blockNo == block.blockNo)
|
||||
continue
|
||||
|
||||
if (b.placeX > block.placeX && b.placeX + b.placeWidth < block.placeX + block.placeWidth && b.placeY > block.placeY && b.placeY + b.placeLength < block.placeY + block.placeLength)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
1300
tests/dev1/dataHandle/common/LayoutEngine/BlockHelper.ts
Normal file
1300
tests/dev1/dataHandle/common/LayoutEngine/BlockHelper.ts
Normal file
File diff suppressed because it is too large
Load Diff
300
tests/dev1/dataHandle/common/LayoutEngine/BlockOverlap.ts
Normal file
300
tests/dev1/dataHandle/common/LayoutEngine/BlockOverlap.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
import { isTargetCurInOrOnSourceCur } from 'cadapi'
|
||||
import type { Polyline } from 'cadapi'
|
||||
import { PolylineHelper } from '../../common/LayoutEngine/PolylineHelper.js'
|
||||
import { PlaceBlock,PlaceBoard,PlaceMaterial } from '../../confClass.js'
|
||||
import { BlockPlus } from './BlockPlus.js'
|
||||
|
||||
|
||||
// 小板干涉判断等算法
|
||||
/** 判断小板干涉情况 */
|
||||
export function checkOverlapInBoard(pm: PlaceMaterial, pb: PlaceBoard,config:any) {
|
||||
if (pb.blockList.length == 0)
|
||||
return
|
||||
const blocks = pb.blockList.slice(0).sort((a, b) => a.cutOrder - b.cutOrder)
|
||||
let {
|
||||
overlapGap= 0.05
|
||||
} = config
|
||||
// 初始化
|
||||
for (const t of blocks) {
|
||||
t.isOverlap = false
|
||||
t.isOutBoard = false
|
||||
t.pl_border = null // 开料后轮廓(待预铣,同刀辅助 ,不含刀半径) ,内缩一个干涉容差/2
|
||||
t.pl_cutBorder = null // 走刀轮廓
|
||||
t.pl_modelSpaces = null
|
||||
t.pl_modelsOut = null
|
||||
}
|
||||
|
||||
const r = pm.diameter / 2
|
||||
// 干涉容差
|
||||
let _overlapGap = overlapGap + 0.002
|
||||
|
||||
if (_overlapGap < 0)
|
||||
_overlapGap = 0
|
||||
|
||||
const borderOff = pm.cutBorder - _overlapGap / 2 - 0.001
|
||||
// console.log('大板的尺寸 -- borderOff', pm.cutBorder, overlapGap / 2 - 0.001)
|
||||
const minx = borderOff
|
||||
const maxX = pb.width - borderOff
|
||||
const miny = borderOff
|
||||
const maxY = pb.length - borderOff
|
||||
|
||||
// 余料异形大板 的轮廓
|
||||
let boardBorders
|
||||
|
||||
if (pb.isAdnormal()) {
|
||||
if (pb.polyline == null) {
|
||||
pb.polyline = PolylineHelper.create(pb.points, true)
|
||||
}
|
||||
if (_overlapGap > 0) // 有干涉容差, 变大
|
||||
{
|
||||
boardBorders = pb.polyline.GetOffsetCurves(_overlapGap)[0]
|
||||
}
|
||||
else {
|
||||
boardBorders = pb.polyline
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
const block1 = blocks[i]
|
||||
// 1.是否超出大板外
|
||||
if (checkOutOfBoard(block1, boardBorders, minx, maxX, miny, maxY, _overlapGap)) {
|
||||
console.log('超出大板外', block1.blockNo)
|
||||
block1.isOverlap = true
|
||||
}
|
||||
// 2.检查板 铣刀路径,是否与前面板的铣刀路径 相干涉
|
||||
for (let p = 0; p < i; p++) {
|
||||
const block2 = blocks[p]
|
||||
|
||||
const isOverlap = isOverlap_block(block1, block2, r, _overlapGap)
|
||||
if (isOverlap) {
|
||||
console.log('铣刀路径问题 检查板 铣刀路径,与前面板的铣刀路径 相干涉', block1.blockNo, block1.blockId, block2.blockNo, block2.blockId)
|
||||
block1.isOverlap = true
|
||||
block2.isOverlap = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
for (const t of blocks) {
|
||||
t.pl_border = null // 开料后轮廓(待预铣,同刀辅助 ,不含刀半径) ,内缩一个干涉容差/2
|
||||
t.pl_cutBorder = null // 走刀轮廓
|
||||
t.pl_modelSpaces = null // 内部轮廓
|
||||
t.pl_modelsOut = null // 造型外扩 轮廓
|
||||
}
|
||||
}
|
||||
|
||||
/** 判断板是否超出大板外 p0:大板可用最低点,p1:大板可用最高点 */
|
||||
function checkOutOfBoard(b: PlaceBlock, pl_board: Polyline, minx, maxx, miny, maxy, overlapGap): boolean {
|
||||
// 判断小板的开料轮廓
|
||||
const pl_block = getBorder(b, overlapGap)
|
||||
|
||||
// 异形的余料大板
|
||||
if (pl_board) {
|
||||
// 小板面积比大板大 , 干涉
|
||||
if (pl_board.Area < pl_block.Area) {
|
||||
console.log('小板面积比大板大 , 干涉')
|
||||
return true
|
||||
}
|
||||
|
||||
// 盒子 不干涉,小板在外面
|
||||
if (pl_board.BoundingBox.intersectsBox(pl_block.BoundingBox) == false) {
|
||||
console.log('盒子 不干涉,小板在外面')
|
||||
return true
|
||||
}
|
||||
// 大盒子,没有包含 小盒子, 肯定在大板外 ,
|
||||
if (pl_board.BoundingBox.containsBox(pl_block.BoundingBox) == false) {
|
||||
console.log('大盒子,没有包含 小盒子, 肯定在大板外 ,')
|
||||
return true
|
||||
}
|
||||
// 轮廓有干涉
|
||||
if (pl_block.IntersectWith(pl_board, 0).length > 0) {
|
||||
console.log('轮廓有干涉')
|
||||
return true
|
||||
}
|
||||
|
||||
// 大轮廓 包含 小轮廓 ,
|
||||
if (isTargetCurInOrOnSourceCur(pl_board, pl_block) == false) {
|
||||
console.log('大轮廓 包含 小轮廓 ')
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
else // 矩形板
|
||||
{
|
||||
// console.log('打印所有的矩形板=>', b.blockNo, pl_block)
|
||||
if (pl_block.BoundingBox.min.x < minx)
|
||||
{
|
||||
console.log('矩形板 小板最小X 小于限定的 minx ')
|
||||
return true
|
||||
}
|
||||
if (pl_block.BoundingBox.min.y < miny)
|
||||
{
|
||||
console.log('矩形板 小板最小y 小于限定的 miny ', b.blockNo, pl_block.BoundingBox.min.y, miny)
|
||||
return true
|
||||
}
|
||||
if (pl_block.BoundingBox.max.x > maxx)
|
||||
{
|
||||
console.log('矩形板 小板最大x 小于限定的 maxx ')
|
||||
return true
|
||||
}
|
||||
if (pl_block.BoundingBox.max.y > maxy)
|
||||
{
|
||||
console.log('矩形板 小板最大y 大于限定的 maxy ')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/** 判断两板是否重叠 */
|
||||
function isOverlap_block(block1: PlaceBlock, block2: PlaceBlock, r: number, overlapGap: number): boolean {
|
||||
if (block1.boardId != block2.boardId)
|
||||
{
|
||||
console.log('板材都不一样',block1.boardId ,block2.boardId);
|
||||
return false // 两板不在同一个大板上
|
||||
}
|
||||
|
||||
// 判断 b1,b2 开料是否干涉, <包括 造型洞内>
|
||||
if (isOverLap_border(block1, block2, r, overlapGap)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 判断b1造型走刀 干涉到 b2。
|
||||
if (isOverlap_model(block1, block2, r, overlapGap)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 判断b2造型走刀 干涉到 b1。
|
||||
if (isOverlap_model(block2, block1, r, overlapGap)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** 开料轮廓是否干涉 */
|
||||
function isOverLap_border(block1: PlaceBlock, block2: PlaceBlock, r: number, overlapGap: number): boolean {
|
||||
let b1 = block1
|
||||
let b2 = block2
|
||||
|
||||
let pl_b1 = getCutBorder(b1, r, overlapGap)
|
||||
let pl_b2 = getCutBorder(b2, r, overlapGap)
|
||||
|
||||
// 盒子没有交集 ,不干涉
|
||||
if (pl_b1.BoundingBox.intersectsBox(pl_b2.BoundingBox) == false)
|
||||
return false
|
||||
// 盒子有交集 ,有可能干涉
|
||||
|
||||
// 走刀轮廓有干涉点 , 肯定干涉了
|
||||
if (pl_b1.IntersectWith(pl_b2, 0).length > 0) {
|
||||
// console.log('走刀轮廓有干涉点 , 肯定干涉了')
|
||||
return true
|
||||
}
|
||||
// 强制 b1 比 b2 大
|
||||
if (pl_b1.Area < pl_b2.Area) {
|
||||
[b1, b2] = [b2, b1];
|
||||
[pl_b1, pl_b2] = [pl_b2, pl_b1]
|
||||
}
|
||||
|
||||
// b1 ,包含 b2
|
||||
if (isTargetCurInOrOnSourceCur(pl_b1, pl_b2)) {
|
||||
// b1 的挖穿空间
|
||||
const pls_inner1 = getModelSpaces(b1, r, overlapGap)
|
||||
// b2 的开料轮廓
|
||||
const pl_border2 = getBorder(b2, overlapGap)
|
||||
for (const pl_1 of pls_inner1) {
|
||||
// 挖穿造型 包含 b2 , 则不干涉
|
||||
if (pl_1.Area > pl_border2.Area && isTargetCurInOrOnSourceCur(pl_1, pl_border2))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** 判断block1 的造型走刀 是否干涉到 block2 */
|
||||
function isOverlap_model(block1: PlaceBlock, block2: PlaceBlock, r: number, overlapGap: number): boolean {
|
||||
// 外扩造型线
|
||||
const pls_1 = getModelsOuter(block1, r, overlapGap)
|
||||
|
||||
const pl_2 = getCutBorder(block2, r, overlapGap)
|
||||
|
||||
for (const pl of pls_1) {
|
||||
if (pl.IntersectWith(pl_2, 0).length > 0)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** 开料轮廓(待预铣,同刀辅助 ,不含刀半径) ,内缩一个干涉容差/2 */
|
||||
function getBorder(b: PlaceBlock, overlapGap: number): Polyline {
|
||||
let pl_border = b.pl_border
|
||||
|
||||
// if (pl_border == null) {
|
||||
const borders = BlockPlus.getBorder_sameKnife(b)
|
||||
let pl = BlockPlus.borderToPolyline(borders)
|
||||
pl = pl.GetOffsetCurves(-overlapGap / 2)[0]
|
||||
|
||||
pl_border = PolylineHelper.moveTo(pl, b.placeX, b.placeY)
|
||||
|
||||
// b.pl_border = pl_border
|
||||
// }
|
||||
return pl_border
|
||||
}
|
||||
|
||||
/** 走刀轮廓(待预铣,同刀辅助 , 刀半径) ,内缩一个干涉容差/2 */
|
||||
function getCutBorder(b: PlaceBlock, r: number, overlapGap: number): Polyline {
|
||||
let pl_cutBorder = b.pl_cutBorder
|
||||
if (pl_cutBorder == null) {
|
||||
const border = BlockPlus.getCutLines_sameKnife(b)
|
||||
pl_cutBorder = BlockPlus.borderToPolyline(border)
|
||||
pl_cutBorder = pl_cutBorder.GetOffsetCurves(-overlapGap / 2)[0]
|
||||
pl_cutBorder = PolylineHelper.moveTo(pl_cutBorder, b.placeX, b.placeY)
|
||||
b.pl_cutBorder = pl_cutBorder
|
||||
}
|
||||
return pl_cutBorder
|
||||
}
|
||||
|
||||
/** 挖穿造型空间<扣除:刀直径 - 容差/2> */
|
||||
function getModelSpaces(b: PlaceBlock, r: number, overlapGap: number): Polyline[] {
|
||||
let pl_models = b.pl_modelSpaces
|
||||
if (pl_models == null) {
|
||||
let spaces = BlockPlus.getBorders_inner(b)
|
||||
let rs = BlockPlus.getBorders_inner_r(b)
|
||||
|
||||
pl_models = []
|
||||
if (spaces.length > 0 && spaces.length == rs.length) {
|
||||
for (let i = 0; i < spaces.length; i++) {
|
||||
let pl = BlockPlus.borderToPolyline(spaces[i])
|
||||
// 内侧 刀直径 < 造型刀,开料刀 最大值 >
|
||||
const off = Math.max(r, rs[i]) * 2 - overlapGap / 2
|
||||
pl = pl.GetOffsetCurves(-off)[0]
|
||||
if (pl == null)
|
||||
continue
|
||||
pl = PolylineHelper.moveTo(pl, b.placeX, b.placeY)
|
||||
pl_models.push(pl)
|
||||
}
|
||||
}
|
||||
b.pl_modelSpaces = pl_models
|
||||
}
|
||||
return pl_models
|
||||
}
|
||||
|
||||
/** 外扩造型 多段线 */
|
||||
function getModelsOuter(b: PlaceBlock, r: number, overlapGap: number): Polyline[] {
|
||||
let pl_models = b.pl_modelsOut
|
||||
if (pl_models == null) {
|
||||
pl_models = []
|
||||
const pls_org = BlockPlus.getOutModelBorders(b)
|
||||
for (const pl of pls_org) {
|
||||
const npl = PolylineHelper.moveTo(pl, b.placeX, b.placeY)
|
||||
|
||||
pl_models.push(npl)
|
||||
}
|
||||
b.pl_modelsOut = pl_models
|
||||
}
|
||||
return pl_models
|
||||
}
|
2395
tests/dev1/dataHandle/common/LayoutEngine/BlockPlus.ts
Normal file
2395
tests/dev1/dataHandle/common/LayoutEngine/BlockPlus.ts
Normal file
File diff suppressed because it is too large
Load Diff
473
tests/dev1/dataHandle/common/LayoutEngine/BlockSizePlus.ts
Normal file
473
tests/dev1/dataHandle/common/LayoutEngine/BlockSizePlus.ts
Normal file
@@ -0,0 +1,473 @@
|
||||
import type { Line2d } from '../../common/base/CAD.js'
|
||||
import { PlaceStyle,PlaceBlock } from '../../confClass.js'
|
||||
|
||||
/** 有造型需要板外下刀, 特别计算要外偏的偏移值, 在排版的时候要算进开料尺寸 */
|
||||
export class BlockSizePlus
|
||||
{
|
||||
/** 分析板外尺寸偏移:1 预洗, 同刀辅助开料,出板造型刀, 2V刀路 */
|
||||
static analySizeOff(block: PlaceBlock, sysConfig: any)
|
||||
{
|
||||
// //1.预铣 预铣值先设置=1;
|
||||
// this.analyePreCut(block,sysConfig);
|
||||
// //2.同刀辅助
|
||||
// this.analyeSameKnifeToHelpCut(block, sysConfig);
|
||||
// //3.造型刀超出板外
|
||||
// this.analyeModelKnifeR(block);
|
||||
// //3.二维刀路 板外下刀
|
||||
// this.analye2VModels(block);
|
||||
}
|
||||
|
||||
/** 分析1: 预铣尺寸扩展, 分析每条异形边预铣标识 */
|
||||
static analyePreCut(block: PlaceBlock, sysConfig: any)
|
||||
{
|
||||
// 先默认 预洗值为1 ,真正使用时 * 真正的预洗值
|
||||
// let bd = block.SaleBlockDetail;
|
||||
// if (bd.preCutSizeOutOff) return;
|
||||
|
||||
// if(sysConfig.PreCutValue <= 0) //没有启动预铣
|
||||
// {
|
||||
// bd.preCutSizeOutOff = new SizeOutOff();
|
||||
// return ;
|
||||
// }
|
||||
// let width = block.Width;
|
||||
// let length = block.Length;
|
||||
// let zp = 0;
|
||||
// let yp = 0;
|
||||
// let sp = 0;
|
||||
// let xp = 0;
|
||||
// if (block.IsUnRegular == false) //矩形板
|
||||
// {
|
||||
// if (block.BorderLeft > 0.001) zp = 1;
|
||||
// if (block.BorderRight > 0.001) yp = 1;
|
||||
// if (block.BorderUpper > 0.001) sp = 1;
|
||||
// if (block.BorderUnder > 0.001) xp = 1;
|
||||
// }
|
||||
// else //异形板
|
||||
// {
|
||||
// //1 判断异形边 预洗
|
||||
// for (let i = 0; i < bd.OrgPoints.length; i++)
|
||||
// {
|
||||
// let s = i - 1;
|
||||
// let j = i + 1;
|
||||
// if (s == -1) s = bd.OrgPoints.length - 1;
|
||||
// if (j == bd.OrgPoints.length) j = 0;
|
||||
|
||||
// let p0 = bd.OrgPoints[s];
|
||||
// let p1 = bd.OrgPoints[i];
|
||||
// let p2 = bd.OrgPoints[j];
|
||||
|
||||
// p1.needPreCut = checkYX(p0,p1,p2);
|
||||
// setFX(p1,p2);
|
||||
// }
|
||||
// //2 如果是同一水平线,或垂直线的, 有一条不能预铣,所有都不能预铣
|
||||
// // //底
|
||||
// // let pts = bd.OrgPoints.filter(t=>t['fx']==0 && equal(t.PointY,0));
|
||||
// // let noPreCut = pts.some(t=>!t.needPreCut);
|
||||
// // if(noPreCut) pts.forEach(t=>t.needPreCut = false);
|
||||
// // xp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
// // //右
|
||||
// // pts = bd.OrgPoints.filter(t=>t['fx']==1 && equal(t.PointX,width));
|
||||
// // noPreCut = pts.some(t=>!t.needPreCut);
|
||||
// // if(noPreCut) pts.forEach(t=>t.needPreCut = false);
|
||||
// // yp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
// // //上
|
||||
// // pts = bd.OrgPoints.filter(t=>t['fx']==2 && equal(t.PointY,length));
|
||||
// // noPreCut = pts.some(t=>!t.needPreCut);
|
||||
// // if(noPreCut) pts.forEach(t=>t.needPreCut = false);
|
||||
// // sp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
// // //左
|
||||
// // pts = bd.OrgPoints.filter(t=>t['fx']==3 && equal(t.PointX,0));
|
||||
// // noPreCut = pts.some(t=>!t.needPreCut);
|
||||
// // if(noPreCut) pts.forEach(t=>t.needPreCut = false);
|
||||
// // zp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
|
||||
// //3 .内部有缺角的边 ,不能预铣
|
||||
// //底
|
||||
// let pts = bd.OrgPoints.filter(t=>t['fx']==0 && equal(t.PointY,0));
|
||||
// if(pts.length > 1) pts.forEach(t=>t.needPreCut = false); //底的边,有多条线段,表示内部有缺角,全部不能预铣
|
||||
// xp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
// //右
|
||||
// pts = bd.OrgPoints.filter(t=>t['fx']==1 && equal(t.PointX,width));
|
||||
// if(pts.length > 1) pts.forEach(t=>t.needPreCut = false);
|
||||
// yp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
// //上
|
||||
// pts = bd.OrgPoints.filter(t=>t['fx']==2 && equal(t.PointY,length));
|
||||
// if(pts.length > 1) pts.forEach(t=>t.needPreCut = false);
|
||||
// sp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
// //左
|
||||
// pts = bd.OrgPoints.filter(t=>t['fx']==3 && equal(t.PointX,0));
|
||||
// if(pts.length > 1) pts.forEach(t=>t.needPreCut = false);
|
||||
// zp = pts.some(t=>t.needPreCut) ? 1:0;
|
||||
|
||||
// //如果斜边 会影响
|
||||
|
||||
// //3 再计算 扩展 有预铣边的斜线 起点或终点 在这边上的,这边就要扩展
|
||||
// for(let i = 0 ; i < bd.OrgPoints.length ;i++)
|
||||
// {
|
||||
|
||||
// let j = i + 1;
|
||||
// if (j == bd.OrgPoints.length) j = 0;
|
||||
// let p1 = bd.OrgPoints[i];
|
||||
// let p2 = bd.OrgPoints[j];
|
||||
|
||||
// if(p1.needPreCut == false) continue;
|
||||
// //判断 起点
|
||||
// if(p1['fx']==4 && xp == 0 && equal(p1.PointY,0)) xp = 1
|
||||
// if(p1['fx']==5 && yp == 0 && equal(p1.PointX,width)) yp = 1
|
||||
// if(p1['fx']==6 && sp == 0 && equal(p1.PointY,length)) sp = 1
|
||||
// if(p1['fx']==7 && zp == 0 && equal(p1.PointX,0)) zp = 1
|
||||
// //判断终点
|
||||
// if(p1['fx']==4 && yp == 0 && equal(p2.PointX,width)) yp = 1
|
||||
// if(p1['fx']==5 && sp == 0 && equal(p2.PointY,length)) sp = 1
|
||||
// if(p1['fx']==6 && zp == 0 && equal(p2.PointX,0)) zp = 1
|
||||
// if(p1['fx']==7 && xp == 0 && equal(p2.PointY,0)) xp = 1
|
||||
// }
|
||||
// }
|
||||
|
||||
// let sizePlus = new SizeOutOff();
|
||||
// sizePlus.left = zp;
|
||||
// sizePlus.right = yp;
|
||||
// sizePlus.bottom = xp;
|
||||
// sizePlus.top = sp;
|
||||
// sizePlus.width = zp + yp;
|
||||
// sizePlus.length = sp + xp;
|
||||
|
||||
// bd.preCutSizeOutOff = sizePlus;
|
||||
|
||||
// function checkYX(p0,p1,p2) //判断p1是否需要预铣
|
||||
// {
|
||||
// if (p1.Curve != 0) return false; //本身是圆弧
|
||||
// if (p1.SealSize < 0.001) return false;// 本身不封边
|
||||
// if (p0.Curve != 0) return false; //前一段是圆弧
|
||||
// if (p2.Curve != 0) return false;//后一段是圆弧
|
||||
// //p1.p2 只要有一点在板内,就不行
|
||||
// let isIn1 = (p1.PointX > 0.001 && p1.PointX < width - 0.001 && p1.PointY > 0.001 && p1.PointY < length - 0.001);
|
||||
// if (isIn1) return false;
|
||||
// let isIn2 = (p2.PointX > 0.001 && p2.PointX < width - 0.001 && p2.PointY > 0.001 && p2.PointY < length - 0.001);
|
||||
// if (isIn2) return false;
|
||||
// return true; //需要预洗
|
||||
// }
|
||||
|
||||
// function setFX(p1,p2) //设置p1的方向
|
||||
// {
|
||||
// let fx = -1; //向右 0,向上 1,向左 2,向下 3 ,右上 4,左上5,左下6,右下 7
|
||||
// if(p2.PointX > p1.PointX && equal(p2.PointY,p1.PointY))
|
||||
// {
|
||||
// fx = 0;
|
||||
// }
|
||||
// else if(p2.PointX < p1.PointX && equal(p2.PointY,p1.PointY))
|
||||
// {
|
||||
// fx = 2;
|
||||
// }
|
||||
// else if(p2.PointY > p1.PointY && equal(p2.PointX,p1.PointX))
|
||||
// {
|
||||
// fx = 1;
|
||||
// }
|
||||
// else if(p2.PointY < p1.PointY && equal(p2.PointX,p1.PointX))
|
||||
// {
|
||||
// fx = 3;
|
||||
// }
|
||||
// else if(p2.PointX > p1.PointX && p2.PointY > p1.PointY)
|
||||
// {
|
||||
// fx = 4;
|
||||
// }
|
||||
// else if(p2.PointX < p1.PointX && p2.PointY > p1.PointY)
|
||||
// {
|
||||
// fx = 5;
|
||||
// }
|
||||
// else if(p2.PointX < p1.PointX && p2.PointY < p1.PointY)
|
||||
// {
|
||||
// fx = 6;
|
||||
// }
|
||||
// else if(p2.PointX > p1.PointX && p2.PointY < p1.PointY)
|
||||
// {
|
||||
// fx = 7;
|
||||
// }
|
||||
// p1['fx'] = fx;
|
||||
// }
|
||||
}
|
||||
|
||||
// 分析2 同刀辅助
|
||||
static analyeSameKnifeToHelpCut(block: PlaceBlock, sysconfig: any)
|
||||
{
|
||||
// let bDetail = block.SaleBlockDetail;
|
||||
// let isSameKnifeToCut = sysconfig.UseSameKnifeToHelpCut && sysconfig.UseSameKnifeToHelpCutGap > 0;
|
||||
// //未启动同刀辅助, 或 该小板 不需要同刀辅助
|
||||
// if (isSameKnifeToCut == false || bDetail.isNeedHelpCut == false)
|
||||
// {
|
||||
// bDetail.sameKnifeToHelpCutGap = 0;
|
||||
// bDetail.sameKnfieHelpOutOff = new SizeOutOff();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// let gap = sysconfig.UseSameKnifeToHelpCutGap;
|
||||
// bDetail.sameKnifeToHelpCutGap = gap;
|
||||
// bDetail.sameKnfieHelpOutOff = new SizeOutOff({ left: gap, right: gap, under: gap, upper: gap });
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
// 分析3 造型刀 超出板外
|
||||
static analyeModelKnifeR(block: PlaceBlock)
|
||||
{
|
||||
// let bDetail = block.SaleBlockDetail;
|
||||
// if (bDetail.modelKnifeOutOff) return;
|
||||
|
||||
// let outValue = (block.PlaceMetrial.CutDia + block.PlaceMetrial.CutGap) /2;
|
||||
|
||||
// let minX = -outValue;
|
||||
// let maxX = bDetail.CuttingWidth + outValue;
|
||||
// let minY = -outValue;
|
||||
// let maxY = bDetail.CuttingLength + outValue;
|
||||
// //求 造型点 最偏 值
|
||||
// for (let model of bDetail.Models)
|
||||
// {
|
||||
// if(model.isVKnifeModel) continue;
|
||||
// if(model.IsDo==false) continue;
|
||||
// if(model.Depth > block.Thickness - 0.001) continue;
|
||||
// let r = model.KnifeRadius;
|
||||
// for (let mp of model.PointList)
|
||||
// {
|
||||
// if (mp.PointX - r < minX) minX = mp.PointX - r;
|
||||
// if (mp.PointX + r > maxX) maxX = mp.PointX + r;
|
||||
// if (mp.PointY - r < minY) minY = mp.PointY - r;
|
||||
// if (mp.PointY + r > maxY) maxY = mp.PointY + r;
|
||||
// }
|
||||
// }
|
||||
|
||||
// let off = {left:0,right :0,upper:0,under:0};
|
||||
// /**暂时屏蔽 造型外扩 */
|
||||
// // if (minX < - outValue) off.left = (-minX) - outValue;
|
||||
// // if (maxX > bDetail.CuttingWidth + outValue) off.right = maxX - bDetail.CuttingWidth - outValue;
|
||||
// // if (minY < - outValue) off.bottom = (-minY) - outValue;
|
||||
// // if (maxY > bDetail.CuttingLength + outValue) off.top = maxY - bDetail.CuttingLength - outValue;
|
||||
|
||||
// bDetail.modelKnifeOutOff = new SizeOutOff(off);
|
||||
}
|
||||
|
||||
// 分析4 板外下刀 2v刀路
|
||||
static analye2VModels(block: PlaceBlock)
|
||||
{
|
||||
|
||||
// let blockDetail = block.SaleBlockDetail;
|
||||
// if (blockDetail.vKnifeModelSizeOutOff) return;//已存在, 不用重复分析
|
||||
// let sizePlus = new SizeOutOff();
|
||||
|
||||
// let cutR = block.PlaceMetrial.CutDia /2;
|
||||
// let minX = - cutR;
|
||||
// let maxX = block.CuttingWidth + cutR ;
|
||||
// let minY = - cutR;
|
||||
// let maxY = block.CuttingLength + cutR;
|
||||
|
||||
// for(let model of blockDetail.Models)
|
||||
// {
|
||||
// if(!model.IsDo || model.isVKnifeModel ==false ) continue;
|
||||
// if(!model.VLines) continue;
|
||||
// if(model.VLines.length == 0) continue;
|
||||
// for(let vm of model.VLines)
|
||||
// {
|
||||
// let knifeR = vm.knifeRadius;
|
||||
// let points = vm.points.map((t) =>
|
||||
// {
|
||||
// return {
|
||||
// PointX: t.x,
|
||||
// PointY: t.y,
|
||||
// Radius: t.r,
|
||||
// Depth: vm.depth - t.z,
|
||||
// Curve: t.bul,
|
||||
// };
|
||||
// });
|
||||
|
||||
// let isOut = vm.points.some(t=>t.x - knifeR < -cutR)
|
||||
// || vm.points.some(t=>t.x + knifeR > block.CuttingWidth + cutR)
|
||||
// || vm.points.some(t=>t.y - knifeR < -cutR)
|
||||
// || vm.points.some(t=>t.y + knifeR > block.CuttingLength + cutR);
|
||||
// if(isOut ) //超出板外
|
||||
// {
|
||||
// vm['isOut'] = true;
|
||||
// for (let mp of points)
|
||||
// {
|
||||
// if (mp.PointX - knifeR < minX) minX = mp.PointX - knifeR;
|
||||
// if (mp.PointX + knifeR > maxX) maxX = mp.PointX + knifeR;
|
||||
// if (mp.PointY - knifeR < minY) minY = mp.PointY - knifeR;
|
||||
// if (mp.PointY + knifeR > maxY) maxY = mp.PointY + knifeR;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (minX < -cutR) sizePlus.left = -minX;
|
||||
// if (maxX > block.CuttingWidth + cutR) sizePlus.right = maxX - block.CuttingWidth;
|
||||
// if (minY < -cutR) sizePlus.bottom = - minY;
|
||||
// if (maxY > block.CuttingLength + cutR) sizePlus.top = maxY - block.CuttingLength;
|
||||
|
||||
// sizePlus.width = sizePlus.left + sizePlus.right;
|
||||
// sizePlus.length = sizePlus.bottom + sizePlus.top;
|
||||
// sizePlus.hasDone = true;
|
||||
|
||||
// //blockDetail.vKnifeModelSizeOutOff = sizePlus;
|
||||
// blockDetail.vKnifeModelSizeOutOff = new SizeOutOff();
|
||||
}
|
||||
|
||||
/** 获得板件偏移值 */
|
||||
static getOffDis(block: PlaceBlock, placeStyle?: PlaceStyle): any
|
||||
{
|
||||
// console.log('获得板件偏移值')
|
||||
if (placeStyle == null || placeStyle == undefined)
|
||||
{
|
||||
placeStyle = block.placeStyle
|
||||
}
|
||||
|
||||
let expandSize :any = block.sizeExpand()
|
||||
let posOff = { x: 0, y: 0, left: 0, right: 0, top: 0, bottom: 0 }
|
||||
if(expandSize){
|
||||
switch (placeStyle)
|
||||
{
|
||||
case PlaceStyle.FRONT: // 正面
|
||||
posOff.x = expandSize.left
|
||||
posOff.y = expandSize.bottom
|
||||
posOff.left = expandSize.left
|
||||
posOff.right = expandSize.right
|
||||
posOff.bottom = expandSize.bottom
|
||||
posOff.top = expandSize.top
|
||||
break
|
||||
case PlaceStyle.FRONT_TURN_RIGHT: // 正面右转
|
||||
posOff.x = expandSize.bottom
|
||||
posOff.y = expandSize.right
|
||||
posOff.left = expandSize.bottom
|
||||
posOff.right = expandSize.top
|
||||
posOff.bottom = expandSize.right
|
||||
posOff.top = expandSize.left
|
||||
break
|
||||
case PlaceStyle.FRONT_TURN_BACK: // 正面后转
|
||||
posOff.x = expandSize.right
|
||||
posOff.y = expandSize.top
|
||||
posOff.left = expandSize.right
|
||||
posOff.right = expandSize.left
|
||||
posOff.bottom = expandSize.top
|
||||
posOff.top = expandSize.bottom
|
||||
break
|
||||
case PlaceStyle.FRONT_TURN_LEFT: // 正面左转
|
||||
posOff.x = expandSize.top
|
||||
posOff.y = expandSize.left
|
||||
posOff.left = expandSize.top
|
||||
posOff.right = expandSize.bottom
|
||||
posOff.bottom = expandSize.left
|
||||
posOff.top = expandSize.right
|
||||
break
|
||||
case PlaceStyle.BACK: // 反面
|
||||
posOff.x = expandSize.right
|
||||
posOff.y = expandSize.bottom
|
||||
posOff.left = expandSize.right
|
||||
posOff.right = expandSize.left
|
||||
posOff.bottom = expandSize.bottom
|
||||
posOff.top = expandSize.top
|
||||
break
|
||||
case PlaceStyle.BACK_TURN_RIGHT: // 反面右转
|
||||
posOff.x = expandSize.bottom
|
||||
posOff.y = expandSize.left
|
||||
posOff.left = expandSize.bottom
|
||||
posOff.right = expandSize.top
|
||||
posOff.bottom = expandSize.left
|
||||
posOff.top = expandSize.right
|
||||
break
|
||||
case PlaceStyle.BACK_TURN_BACK: // 反面后转
|
||||
posOff.x = expandSize.left
|
||||
posOff.y = expandSize.top
|
||||
posOff.left = expandSize.left
|
||||
posOff.right = expandSize.right
|
||||
posOff.bottom = expandSize.top
|
||||
posOff.top = expandSize.bottom
|
||||
break
|
||||
case PlaceStyle.BACK_TURN_LEFT: // 反面左转
|
||||
posOff.x = expandSize.top
|
||||
posOff.y = expandSize.right
|
||||
posOff.left = expandSize.bottom
|
||||
posOff.right = expandSize.bottom
|
||||
posOff.bottom = expandSize.right
|
||||
posOff.top = expandSize.left
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return posOff
|
||||
}
|
||||
|
||||
// 设置板件的位置
|
||||
static resetNewPlace(block: PlaceBlock)
|
||||
{
|
||||
let posOff = this.getOffDis(block)
|
||||
block.placeOffX = posOff.x
|
||||
block.placeOffY = posOff.y
|
||||
block.placeX = block.placeX + block.placeOffX
|
||||
block.placeY = block.placeY + block.placeOffY
|
||||
}
|
||||
|
||||
static checkPreBorder(block: PlaceBlock, line: Line2d): boolean // 判断 开料刀路中的一条 line 是否需要预洗
|
||||
{
|
||||
let x1 = line.StartPoint.m_X
|
||||
let y1 = line.StartPoint.m_Y
|
||||
let x2 = line.EndPoint.m_X
|
||||
let y2 = line.EndPoint.m_Y
|
||||
|
||||
if (block.isUnRegular == false) // 矩形
|
||||
{
|
||||
if (this.eqaul(x1, 0, 0.01) && this.eqaul(x1, x2))
|
||||
return block.sealLeft > 0
|
||||
if (this.eqaul(x1, block.cutWidth, 0.01) && this.eqaul(x1, x2))
|
||||
return block.sealRight > 0
|
||||
if (this.eqaul(y1, 0, 0.01) && this.eqaul(y1, y2))
|
||||
return block.sealBottom > 0
|
||||
if (this.eqaul(y1, block.cutLength, 0.01) && this.eqaul(y1, y2))
|
||||
return block.sealTop > 0
|
||||
|
||||
return false
|
||||
}
|
||||
else // 异形
|
||||
{
|
||||
// 找出原始轮廓中 对应的边 , 是否有 预洗信息
|
||||
for (let i = 0; i < block.orgPoints.length; i++)
|
||||
{
|
||||
let j = i + 1
|
||||
if (j == block.orgPoints.length)
|
||||
j = 0
|
||||
|
||||
if (block.orgPoints[i].curve != 0)
|
||||
continue
|
||||
let dis = block.orgPoints[i].sealSize
|
||||
|
||||
let w1 = block.orgPoints[i].pointX - (block.blockDetail?.offsetX || 0)
|
||||
let v1 = block.orgPoints[i].pointY - (block.blockDetail?.offsetY || 0)
|
||||
let w2 = block.orgPoints[j].pointX - (block.blockDetail?.offsetX || 0)
|
||||
let v2 = block.orgPoints[j].pointY - (block.blockDetail?.offsetY || 0)
|
||||
|
||||
let dis1 = Math.sqrt((x1 - w1) * (x1 - w1) + (y1 - v1) * (y1 - v1))
|
||||
if (dis1 > dis * 2)
|
||||
continue
|
||||
let dis2 = Math.sqrt((x2 - w2) * (x2 - w2) + (y2 - v2) * (y2 - v2))
|
||||
if (dis2 < dis * 2)
|
||||
return block.orgPoints[i].isPreCutRequired
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static equal2Point(p1, p2, dis = 0.001)
|
||||
{
|
||||
let x1 = p1.m_X
|
||||
let y1 = p1.m_Y
|
||||
let x2 = p2.m_X
|
||||
let y2 = p2.m_Y
|
||||
let len1 = (x1 - x2) * (x1 - x2)
|
||||
let len2 = (y1 - y2) * (y1 - y2)
|
||||
let len = Math.sqrt(len2 + len1)
|
||||
return len < dis
|
||||
}
|
||||
|
||||
static eqaul(a, b, dis = 0.001)
|
||||
{
|
||||
return Math.abs(a - b) < 0.001
|
||||
}
|
||||
}
|
266
tests/dev1/dataHandle/common/LayoutEngine/Curves2Parts.ts
Normal file
266
tests/dev1/dataHandle/common/LayoutEngine/Curves2Parts.ts
Normal file
@@ -0,0 +1,266 @@
|
||||
import type { PolylineProps } from 'cadapi'
|
||||
import { Circle, Polyline, Polyline2Points } from 'cadapi'
|
||||
import { EndType, JoinType } from 'js-angusj-clipper/web'
|
||||
import type { Box3, Vector3 } from 'three'
|
||||
import { Vector2 } from 'three'
|
||||
import { clipperCpp } from '../ClipperCpp'
|
||||
import type { Point } from '../Vector2'
|
||||
import { Path, PathScale } from '../core/Path'
|
||||
import type { IOffset } from './Simplify2'
|
||||
import { SimplifyDouglasPeucker } from './Simplify2'
|
||||
|
||||
/** 内外接多边形 */
|
||||
export function Circle2Points(circle: Circle, knifRadius: number, splitSize = 10, outside = false): Point[] {
|
||||
let radius = circle.Radius
|
||||
const an = Math.PI * 2 / splitSize
|
||||
|
||||
if (outside)
|
||||
radius = radius / Math.cos(an / 2) + knifRadius
|
||||
else
|
||||
radius -= knifRadius
|
||||
|
||||
const cenP = circle.Center
|
||||
const pts: Vector3[] = []
|
||||
for (let i = 0; i < splitSize; i++)
|
||||
pts.push(polar(cenP.clone(), an * i, radius))
|
||||
|
||||
return pts as Point[]
|
||||
}
|
||||
|
||||
export function Curve2Path(curve: Polyline, outside = false): Path {
|
||||
if (curve.IsClockWise)
|
||||
curve.Reverse()
|
||||
const w = new CurveWrap(curve, 3, outside)
|
||||
return new Path(outside ? w.GetOutsidePoints() : w.GetInsidePoints())
|
||||
}
|
||||
|
||||
export class CurveWrap {
|
||||
BoundingBox: Box3
|
||||
|
||||
Area: number
|
||||
|
||||
SimplyPolyline?: Polyline
|
||||
SimplyOffset?: IOffset
|
||||
Used = false
|
||||
Holes: CurveWrap[] = []
|
||||
|
||||
Points: Point[] = []
|
||||
|
||||
constructor(public Curve: Polyline | Circle, public KnifRadius: number, public IsOutside: boolean) {
|
||||
this.BoundingBox = Curve.BoundingBox
|
||||
this.Area = 0
|
||||
if (Curve instanceof Polyline) {
|
||||
const pts = Polyline2Points(Curve, IsOutside, 0)[1]
|
||||
/**
|
||||
* 精简算法SimplifyDouglasPeucker 会导致轮廓变大,
|
||||
* 修改成直接取点 陈雄 QQ聊天记录 23.9.18
|
||||
* 23.10.9 by lrx
|
||||
*/
|
||||
|
||||
const [spts, offset] = SimplifyDouglasPeucker(pts, KnifRadius ** 2 + KnifRadius)
|
||||
if (spts.length !== pts.length && spts.length > 2) {
|
||||
this.SimplyOffset = offset
|
||||
|
||||
this.SimplyPolyline = Path2Polyline(spts)
|
||||
this.Curve = this.SimplyPolyline// 保险起见,也更新它
|
||||
this.Area = this.SimplyPolyline.Area
|
||||
}
|
||||
else// 此处更新多段线
|
||||
{ this.Curve = Path2Polyline(pts) }
|
||||
this.Points = spts
|
||||
|
||||
// 以下修改后的
|
||||
// this.Curve = Path2Polyline(pts);
|
||||
// this.Points = pts;
|
||||
}
|
||||
|
||||
if (this.Area === undefined)
|
||||
this.Area = this.Curve.Area
|
||||
}
|
||||
|
||||
ContainsCurve(curve: CurveWrap): boolean {
|
||||
if (this.SimplyPolyline)
|
||||
return this.SimplyPolyline.PtInCurve(curve.Curve.StartPoint)
|
||||
return this.Curve.PtInCurve(curve.Curve.StartPoint)
|
||||
}
|
||||
|
||||
GetOutsidePoints(): Point[] {
|
||||
if (this.Curve instanceof Circle) {
|
||||
const pts = Circle2Points(this.Curve, this.KnifRadius, 10, true)
|
||||
return pts
|
||||
}
|
||||
else {
|
||||
const pl = this.SimplyPolyline || this.Curve
|
||||
let offset = this.KnifRadius
|
||||
if (this.SimplyOffset)
|
||||
offset += this.SimplyOffset.positiveOffset
|
||||
|
||||
if (offset > 0) {
|
||||
let pts = pl.GetStretchPoints() as Point[]
|
||||
if (clipperCpp.lib && typeof clipperCpp.lib.offsetToPaths === 'function') {
|
||||
const result = clipperCpp.lib.offsetToPaths({
|
||||
delta: offset * 1e4,
|
||||
offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }],
|
||||
});
|
||||
pts = result && result[0] ? result[0] : [];
|
||||
}
|
||||
|
||||
try {
|
||||
PathScale(pts, 1e-4)
|
||||
}
|
||||
catch {
|
||||
console.log('err')
|
||||
}
|
||||
return pts
|
||||
}
|
||||
else { return this.Points }
|
||||
}
|
||||
}
|
||||
|
||||
GetInsidePoints(): Point[] {
|
||||
return this.GetInsidePoints2(this.KnifRadius)
|
||||
}
|
||||
|
||||
GetInsidePoints2(d: number): Point[] {
|
||||
if (this.Curve instanceof Circle) {
|
||||
const pts = Circle2Points(this.Curve, d, 10, false)
|
||||
return pts
|
||||
}
|
||||
else {
|
||||
const pl = this.SimplyPolyline || this.Curve
|
||||
let offset = -d
|
||||
if (this.SimplyOffset)
|
||||
offset += this.SimplyOffset.negativeOffset
|
||||
|
||||
if (offset < -0.01) {
|
||||
const pls = pl.GetOffsetCurves(offset)
|
||||
if (pls.length)
|
||||
{
|
||||
return pls[0].GetStretchPoints()
|
||||
}else{
|
||||
return []
|
||||
}
|
||||
}
|
||||
else { return this.Points }
|
||||
}
|
||||
}
|
||||
|
||||
/** 引入Polyline 已经含刀半径, 获得精简后的点阵 */
|
||||
GetOrgPoints(outside = true): Point[] {
|
||||
if (this.Curve instanceof Circle) {
|
||||
const pts = Circle2Points(this.Curve, 0, 10, outside)
|
||||
return pts
|
||||
}
|
||||
else {
|
||||
const pl = this.SimplyPolyline || this.Curve
|
||||
let offset = 0
|
||||
if (this.SimplyOffset)
|
||||
offset += this.SimplyOffset.positiveOffset
|
||||
|
||||
if (offset > 0) {
|
||||
let pts = pl.GetStretchPoints() as Point[]
|
||||
if (clipperCpp.lib && typeof clipperCpp.lib.offsetToPaths === 'function') {
|
||||
const result = clipperCpp.lib.offsetToPaths({
|
||||
delta: offset * 1e4,
|
||||
offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }],
|
||||
});
|
||||
pts = result && result[0] ? result[0] : [];
|
||||
}
|
||||
try {
|
||||
PathScale(pts, 1e-4)
|
||||
}
|
||||
catch {
|
||||
console.log('err')
|
||||
}
|
||||
return pts
|
||||
}
|
||||
else {
|
||||
return this.Points
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 多段线 转点整 已弃用,整合到CAD api 23.11.2 */
|
||||
// export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: number): [Polyline, Point[]]
|
||||
// {
|
||||
// let pts: Point[] = [];
|
||||
|
||||
// if (!outside) knifRadius = -knifRadius;
|
||||
// if (pl.IsClockWise) pl.Reverse();
|
||||
// for (let i = 0; i < pl.EndParam; i++)
|
||||
// {
|
||||
// pts.push(pl.GetPointAtParam(i));
|
||||
|
||||
// let bul = pl.GetBulgeAt(i);
|
||||
// if (bul !== 0)
|
||||
// {
|
||||
// let arc = pl.GetCurveAtIndex(i) as Arc;
|
||||
|
||||
// let allAngle = arc.AllAngle;
|
||||
// let arcLength = arc.Length;
|
||||
|
||||
// // let splitCount = Math.round(allAngle / 0.4);
|
||||
// // if (arcLength < 300)
|
||||
// // splitCount = 2;
|
||||
// // else
|
||||
// // splitCount = Math.max(arcLength / 200, splitCount,2);
|
||||
|
||||
// let minCount = Math.floor(allAngle * 4 / Math.PI);
|
||||
// let splitCount = Math.round(allAngle / 0.4);
|
||||
// if (arcLength < 300)
|
||||
// splitCount = Math.max(2, minCount);
|
||||
// else
|
||||
// splitCount = Math.max(Math.floor(arcLength / 200), splitCount,2, minCount);
|
||||
|
||||
// let radius = arc.Radius;
|
||||
// if (outside === bul > 0)
|
||||
// radius = radius / Math.cos(allAngle / (splitCount * 2));
|
||||
|
||||
// let cp = arc.Center;
|
||||
// for (let j = outside ? 0.5 : 0; j < splitCount; j++)
|
||||
// {
|
||||
// let a = arc.GetAngleAtParam(j * (1 / splitCount));
|
||||
// let p = polar(cp.clone(), a, radius);
|
||||
// pts.push(p);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (knifRadius !== 0)
|
||||
// {
|
||||
// pts = clipperCpp.lib.offsetToPaths({
|
||||
// delta: knifRadius * 1e4,
|
||||
// offsetInputs: [{ data: PathScale(pts, 1e4), joinType: JoinType.Miter, endType: EndType.ClosedPolygon }]
|
||||
// })[0];
|
||||
// PathScale(pts, 1e-4);
|
||||
// }
|
||||
// return [pl, pts];
|
||||
// }
|
||||
|
||||
export function Path2Polyline(path: Point[]): Polyline {
|
||||
const pl = new Polyline()
|
||||
pl.LineData = path.map((p) => {
|
||||
return { pt: new Vector2(p.x, p.y), bul: 0 }
|
||||
})
|
||||
pl.CloseMark = true
|
||||
return pl
|
||||
}
|
||||
|
||||
export function Points2Polyline(pts: any[]): Polyline {
|
||||
const lined: PolylineProps[] = []
|
||||
const count = pts.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
const p0 = pts[i]
|
||||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: p0.bul })
|
||||
}
|
||||
const pls = new Polyline(lined)
|
||||
pls.CloseMark = true
|
||||
return pls
|
||||
}
|
||||
|
||||
export function polar<T extends Vector2 | Vector3>(v: T, an: number, dis: number): T {
|
||||
v.x += Math.cos(an) * dis
|
||||
v.y += Math.sin(an) * dis
|
||||
return v
|
||||
}
|
@@ -0,0 +1,321 @@
|
||||
|
||||
import { BlockSizePlus } from './BlockSizePlus.js'
|
||||
import { BlockHelper } from './BlockHelper.js'
|
||||
import { BlockModel,PlaceBlock,PlaceBoard,PlaceMaterial } from '../../confClass.js'
|
||||
|
||||
/**
|
||||
* 大板长边的两侧多少范围内 避免造型 ,如果出现,则那片小板掉头放置
|
||||
* @param pm
|
||||
* @param 大板边缘范围
|
||||
* @face 大板面 反面 1; 正面 0 ;双面 2
|
||||
* @forCNC 针对的是cnc数据
|
||||
* @isReverse 反转: 大板边缘尽量 安排造型
|
||||
*/
|
||||
export function DisPoseModelInBoardBorder(pm: PlaceMaterial, width: number, face: number, forCNC: number, isReverse: boolean)
|
||||
{
|
||||
if (width == 0)
|
||||
return
|
||||
for (const pb of pm.boardList)
|
||||
{
|
||||
if (pb.isAdnormal())
|
||||
continue
|
||||
if (pb.isLocked || pb.cutedType != 0)
|
||||
continue
|
||||
if (isReverse == false)
|
||||
{
|
||||
// 避免大板边出现 造型
|
||||
DisPoseModelInBoardBorder_pb(pb, width, face, forCNC)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 尽量 安排 造型在大板边
|
||||
needModelInBoardBorder_pb(pb, width, face, forCNC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 大板边缘 避免造型 如果出现,则那片小板掉头放置
|
||||
*
|
||||
* @param pb
|
||||
* @param width
|
||||
* @param face
|
||||
* @param forCNC 针对的是cnc的加工
|
||||
*/
|
||||
function DisPoseModelInBoardBorder_pb(pb: PlaceBoard, width: number, face: number, forCNC: number)
|
||||
{
|
||||
const bw = pb.width
|
||||
for (const block of pb.blockList)
|
||||
{
|
||||
if (block.isUnRegular)
|
||||
continue
|
||||
if (block.placeX + block.placeWidth < width)
|
||||
continue // 小板整片板都在边缘, 转不转没意义
|
||||
if (block.placeX > bw - width)
|
||||
continue
|
||||
// 如果内部有小板, 也不能掉头
|
||||
if (hasChildBlock(block, pb.blockList))
|
||||
continue
|
||||
|
||||
let isInBorder = false
|
||||
const models = getModels(block, face, forCNC)
|
||||
for (const model of models)
|
||||
{
|
||||
isInBorder = isInBoardBorder(block, model, bw, width)
|
||||
if (isInBorder)
|
||||
break
|
||||
}
|
||||
|
||||
if (isInBorder == false)
|
||||
continue
|
||||
|
||||
turnBlock(block)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 大板边缘 尽量安排造型
|
||||
*
|
||||
* @param pb
|
||||
* @param width
|
||||
* @param face
|
||||
* @param forCNC
|
||||
*/
|
||||
function needModelInBoardBorder_pb(pb: PlaceBoard, width: number, face: number, forCNC: number)
|
||||
{
|
||||
const blocks_turn: any = []
|
||||
const bw = pb.width
|
||||
|
||||
// 大板左边
|
||||
const blocks_z = pb.blockList.filter(t => t.isUnRegular == false && t.placeX < width)
|
||||
for (const block of blocks_z)
|
||||
{
|
||||
// 如果内部有小板, 也不能掉头
|
||||
if (hasChildBlock(block, pb.blockList))
|
||||
continue
|
||||
// 造型
|
||||
const models = getModels(block, face, forCNC)
|
||||
|
||||
let hasModelOnLeft = false // 左边有造型
|
||||
let hasModelOnRight = false // 右边有造型
|
||||
for (const model of models)
|
||||
{
|
||||
if (hasModelOnLeft == false && isInBoardLeft(block, model, width, false))
|
||||
hasModelOnLeft = true
|
||||
if (hasModelOnRight == false && isInBoardLeft(block, model, width, true))
|
||||
hasModelOnRight = true
|
||||
}
|
||||
// 左边没有造型, 右边有造型,则掉头
|
||||
if (hasModelOnLeft == false && hasModelOnRight)
|
||||
{
|
||||
blocks_turn.push(block)
|
||||
}
|
||||
}
|
||||
|
||||
// 大板右边
|
||||
const blocks_y = pb.blockList.filter(t => t.isUnRegular == false && t.placeX + t.placeWidth > bw - width)
|
||||
for (const block of blocks_y)
|
||||
{
|
||||
// 如果内部有小板, 也不能掉头
|
||||
if (hasChildBlock(block, pb.blockList))
|
||||
continue
|
||||
// 造型
|
||||
const models = getModels(block, face, forCNC)
|
||||
|
||||
let hasModelOnLeft = false // 小板左边有造型
|
||||
let hasModelOnRight = false // 小板右边有造型
|
||||
for (const model of models)
|
||||
{
|
||||
if (hasModelOnLeft == false && isInBoardRight(block, model, bw, width, true))
|
||||
hasModelOnLeft = true
|
||||
if (hasModelOnRight == false && isInBoardRight(block, model, bw, width, false))
|
||||
hasModelOnRight = true
|
||||
}
|
||||
// 右边没有造型, 左边有造型,则掉头
|
||||
if (hasModelOnLeft && hasModelOnRight == false)
|
||||
{
|
||||
blocks_turn.push(block)
|
||||
}
|
||||
}
|
||||
|
||||
// 翻转小板
|
||||
for (const block of blocks_turn)
|
||||
{
|
||||
turnBlock(block)
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取小板指定面造型 */
|
||||
function getModels(block: PlaceBlock, face: number, forCNC: number): BlockModel[]
|
||||
{
|
||||
let models: any = []
|
||||
if (face == 0) // 正面
|
||||
{
|
||||
models = block.modelListFaceA
|
||||
if (forCNC == 1)
|
||||
{
|
||||
const ms_A = block.isTurnOver() == false ? block.modelListOrgFaceA() : block.modelListOrgFaceB()
|
||||
models = ms_A.filter(t => t.isCutting == false).concat(block.modelListOrgTrough().filter(t => t.isCutting == false))
|
||||
}
|
||||
}
|
||||
else if (face == 1) // 反面
|
||||
{
|
||||
models = block.modelListFaceB
|
||||
if (forCNC == 1)
|
||||
{
|
||||
const ms_B = block.isTurnOver() == false ? block.modelListOrgFaceB() : block.modelListOrgFaceA()
|
||||
models = ms_B.filter(t => t.isCutting == false)
|
||||
}
|
||||
}
|
||||
else // 随意面
|
||||
{
|
||||
models = block.models().filter(t => t.isCutting != (forCNC == 1))
|
||||
}
|
||||
return models
|
||||
}
|
||||
|
||||
/** 判断是否有造型出现在无法加工的区域 */
|
||||
export function hasModelInBoardBorder(pb: PlaceBoard, width: number, face: number, forCNC: number)
|
||||
{
|
||||
pb.hasModelOnLeft = false
|
||||
pb.hasModelOnRight = false
|
||||
|
||||
const bw = pb.width
|
||||
for (const block of pb.blockList)
|
||||
{
|
||||
if (block.isUnRegular)
|
||||
continue
|
||||
if (block.placeX + block.placeWidth < width)
|
||||
continue // 小板整片板都在边缘, 转不转没意义
|
||||
if (block.placeX > bw - width)
|
||||
continue
|
||||
|
||||
const models = getModels(block, face, forCNC)
|
||||
|
||||
if (pb.hasModelOnLeft == false)
|
||||
{
|
||||
for (const model of models)
|
||||
{
|
||||
let isLeft = false
|
||||
for (const p of model.pointList)
|
||||
{
|
||||
const rp = BlockHelper.getPlaceXYInBoard(block, p.pointX, p.pointY)
|
||||
if (rp.x < width)
|
||||
{
|
||||
isLeft = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (isLeft)
|
||||
{
|
||||
pb.hasModelOnLeft = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pb.hasModelOnRight == false)
|
||||
{
|
||||
for (const model of models)
|
||||
{
|
||||
let isRight = false
|
||||
for (const p of model.pointList)
|
||||
{
|
||||
const rp = BlockHelper.getPlaceXYInBoard(block, p.pointX, p.pointY)
|
||||
if (rp.x > bw - width)
|
||||
{
|
||||
isRight = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (isRight)
|
||||
{
|
||||
pb.hasModelOnRight = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pb.hasModelOnLeft && pb.hasModelOnRight)
|
||||
return // 已经有结果了
|
||||
}
|
||||
}
|
||||
|
||||
/** 判断造型是否在大板的边缘 */
|
||||
function isInBoardBorder(block: PlaceBlock, model: BlockModel, bw: number, width: number): boolean
|
||||
{
|
||||
for (const p of model.pointList)
|
||||
{
|
||||
const rp = BlockHelper.getPlaceXYInBoard(block, p.pointX, p.pointY)
|
||||
if (rp.x < width || rp.x > bw - width)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** 造型是否在大板左边 toTurn 翻转后 */
|
||||
function isInBoardLeft(block: PlaceBlock, model: BlockModel, width: number, toTurn: boolean)
|
||||
{
|
||||
for (const p of model.pointList)
|
||||
{
|
||||
const rp = BlockHelper.getPlaceXYInBlock(block, p.pointX, p.pointY, false, false)
|
||||
let rx = toTurn ? block.placeWidth - rp.x : rp.x
|
||||
rx = rx + block.placeX
|
||||
if (rx < width)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** 造型 是否在大板 右边 toTurn 翻转后 */
|
||||
function isInBoardRight(block: PlaceBlock, model: BlockModel, boardWidth: number, width: number, toTurn: boolean)
|
||||
{
|
||||
for (const p of model.pointList)
|
||||
{
|
||||
const rp = BlockHelper.getPlaceXYInBlock(block, p.pointX, p.pointY, false, false)
|
||||
let rx = toTurn ? block.placeWidth - rp.x : rp.x
|
||||
rx = rx + block.placeX
|
||||
if (rx > boardWidth - width)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** 判断block里有没有小板 */
|
||||
function hasChildBlock(block: PlaceBlock, blocks: PlaceBlock[]): boolean
|
||||
{
|
||||
for (const b of blocks)
|
||||
{
|
||||
if (b.blockNo == block.blockNo)
|
||||
continue
|
||||
|
||||
if (b.placeX > block.placeX && b.placeX + b.placeWidth < block.placeX + block.placeWidth && b.placeY > block.placeY && b.placeY + b.placeLength < block.placeY + block.placeLength)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function turnBlock(block: PlaceBlock)
|
||||
{
|
||||
// 有造型在大板边
|
||||
|
||||
const orgStyle = block.placeStyle
|
||||
const newStyle = BlockHelper.getTurnedPlaceStyle(orgStyle, 2)
|
||||
|
||||
const orgPlaceX = block.placeX - block.placeOffX
|
||||
const orgPlaceY = block.placeY - block.placeOffY
|
||||
|
||||
const offset = BlockSizePlus.getOffDis(block, newStyle)
|
||||
|
||||
// 左右上下,外扩尺寸不一样, 翻转有可能会导致 小板与其他小板 干涉。
|
||||
if (offset.left != offset.right || offset.top != offset.bottom)
|
||||
return
|
||||
|
||||
const newPlaceX = orgPlaceX + offset.x
|
||||
const newPlaceY = orgPlaceY + offset.y
|
||||
block.placeX = newPlaceX
|
||||
block.placeY = newPlaceY
|
||||
block.placeOffX = offset.x
|
||||
block.placeOffY = offset.y
|
||||
|
||||
// 修改小板的放置方式
|
||||
BlockHelper.resetPlaceStyle(block, newStyle)
|
||||
}
|
94
tests/dev1/dataHandle/common/LayoutEngine/PlaceBase.ts
Normal file
94
tests/dev1/dataHandle/common/LayoutEngine/PlaceBase.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { ArrayExt } from '../../common/base/ArrayExt.js';
|
||||
import { PlaceBoard,PlaceMaterial } from '../../confClass.js';
|
||||
import { hasModelInBoardBorder } from './DisposeModelInBoardBorder.js';
|
||||
import { CutOrder } from '../cutorder/CutOrder.js';
|
||||
// import { PlaceStore } from './PlaceStore.js';
|
||||
|
||||
/**
|
||||
* 返回大板信息
|
||||
* @param pm
|
||||
* @param bid
|
||||
*/
|
||||
export function getPlaceBoard(pm: PlaceMaterial, bid: number): PlaceBoard
|
||||
{
|
||||
if (bid < pm.minBoardId) return null;
|
||||
if (bid > pm.maxBoardId) return null;
|
||||
let pb = pm.boardList.find(t => t.boardId == bid);
|
||||
if (pb == null)
|
||||
{
|
||||
pb = new PlaceBoard(bid, pm.width, pm.length);
|
||||
pb.blockList = pm.blockList.filter(t => t.boardId == bid);
|
||||
resetPlaceBoard(pm, pb);
|
||||
pm.boardList.push(pb);
|
||||
}
|
||||
return pb;
|
||||
}
|
||||
|
||||
/**重设大板汇总 */
|
||||
export function resetPlaceBoard(pm: PlaceMaterial, pb: PlaceBoard, blocks = null)
|
||||
{
|
||||
if (pm == null) return;
|
||||
if (pb == null) return;
|
||||
if (blocks != null)
|
||||
{
|
||||
pb.blockList = blocks;
|
||||
}
|
||||
|
||||
pb.blockCount = pb.blockList.length;
|
||||
pb.blockArea = ArrayExt.sum(pb.blockList, t => t.area);
|
||||
pb.usageRate = 100 * pb.blockArea / pb.area;
|
||||
|
||||
//判断 有造型出现在无法加工的区域
|
||||
// const sys = PlaceStore.sysConfig;
|
||||
// if(sys && sys.boardBorderModelRange > 1 && sys.modelNearBoardBorder == false)
|
||||
// {
|
||||
// hasModelInBoardBorder(pb, sys.boardBorderModelRange, sys.boardBorderModelModeToFace, sys.boardBorderModelByMachine);
|
||||
// }
|
||||
// CutOrder.autoCalcCutOrder(pm, pb);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重设板材 优化率等, 是否排序大板列表
|
||||
* @param pm
|
||||
* @param sortBoard
|
||||
*/
|
||||
export function resetPlaceMaterial(pm: PlaceMaterial, sortBoard = false)
|
||||
{
|
||||
if (pm.boardCount == 0)
|
||||
{
|
||||
pm.blockCount = 0;
|
||||
pm.blockArea = 0;
|
||||
pm.avgUsageRateAll = 0;
|
||||
pm.avgUsageRateExcludeLastBoard = 0;
|
||||
pm.usageRateLastBoard = 0;
|
||||
pm.boardCountFlipFace = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
pm.blockCount = pm.blockList.length;
|
||||
pm.blockArea = ArrayExt.sum(pm.blockList, t => t.area);
|
||||
if (pm.boardCount == 1)
|
||||
{
|
||||
pm.avgUsageRateAll = pm.blockArea;
|
||||
pm.avgUsageRateExcludeLastBoard = pm.blockArea;
|
||||
pm.usageRateLastBoard = pm.blockArea;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_last = getPlaceBoard(pm, pm.maxBoardId).blockArea;
|
||||
pm.avgUsageRateAll = pm.blockArea / pm.boardCount;
|
||||
pm.avgUsageRateExcludeLastBoard = (pm.blockArea - size_last) / (pm.boardCount - 1);
|
||||
pm.usageRateLastBoard = size_last;
|
||||
if (sortBoard) sortBoardPlace(pm);
|
||||
}
|
||||
pm.boardCountFlipFace = ArrayExt.count(pm.boardList, t => t.isTwoFaceProcessing);
|
||||
}
|
||||
|
||||
/**
|
||||
* 排序大板信息
|
||||
* @param pm
|
||||
*/
|
||||
export function sortBoardPlace(pm: PlaceMaterial)
|
||||
{
|
||||
pm.boardList = ArrayExt.sortBy(pm.boardList, t => t.boardId);
|
||||
}
|
359
tests/dev1/dataHandle/common/LayoutEngine/PolylineHelper.ts
Normal file
359
tests/dev1/dataHandle/common/LayoutEngine/PolylineHelper.ts
Normal file
@@ -0,0 +1,359 @@
|
||||
import type { PolylineProps } from 'cadapi'
|
||||
import { CADFiler, Circle, Polyline, Status, VKnifToolPath, isTargetCurInOrOnSourceCur } from 'cadapi'
|
||||
import type { Box3 } from 'three'
|
||||
import { Vector2, Vector3 } from 'three'
|
||||
import { arrayRemoveDuplicateBySort } from '../ArrayExt'
|
||||
import type { Curve2d } from '../../common/base/CAD'
|
||||
import { Arc2d, Point2d, copyTextToClipboard } from '../../common/base/CAD'
|
||||
import { CurveWrap } from './Curves2Parts'
|
||||
|
||||
// import type { Curve2d } from '../../common/base/CAD'
|
||||
|
||||
export class PolylineHelper {
|
||||
/** 创建闭合多段线 */
|
||||
static create(pts: any[], closeMark = false): Polyline {
|
||||
let lined: PolylineProps[] = []
|
||||
let count = pts.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
let p0 = pts[i]
|
||||
|
||||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: p0.bul || 0 })
|
||||
}
|
||||
let pls = new Polyline(lined)
|
||||
pls.CloseMark = closeMark
|
||||
return pls
|
||||
}
|
||||
|
||||
static createByCurve2d(curs: Curve2d[]): Polyline {
|
||||
let lined: PolylineProps[] = []
|
||||
for (let cur of curs) {
|
||||
let x = cur.StartPoint.m_X
|
||||
let y = cur.StartPoint.m_Y
|
||||
let bul = 0
|
||||
if (cur instanceof Arc2d)
|
||||
bul = cur.Bul || 0
|
||||
lined.push({ pt: new Vector2(x, y), bul })
|
||||
}
|
||||
let pls = new Polyline(lined)
|
||||
pls.CloseMark = true
|
||||
return pls
|
||||
}
|
||||
|
||||
static createByPts(pts: any[], buls: number[], closeMark = false): Polyline {
|
||||
let plps: PolylineProps[] = []
|
||||
let count = pts.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
let p0 = pts[i]
|
||||
plps.push({ pt: new Vector2(p0.x, p0.y), bul: buls[i] })
|
||||
}
|
||||
let pls = new Polyline(plps)
|
||||
pls.CloseMark = closeMark
|
||||
return pls
|
||||
}
|
||||
|
||||
static getSimplePoints(pts: any[], offset: number): any[] {
|
||||
let pl = PolylineHelper.create(pts)
|
||||
pl.CloseMark = true
|
||||
let cureW = new CurveWrap(pl, offset, true)
|
||||
let pts2 = cureW.GetOutsidePoints()
|
||||
arrayRemoveDuplicateBySort(pts2, (p1, p2) => (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) < 1e-2)
|
||||
return pts2
|
||||
}
|
||||
|
||||
static createByWidthLength(w: number, l: number): Polyline {
|
||||
let plps: PolylineProps[] = []
|
||||
plps.push({ pt: new Vector2(0, 0), bul: 0 })
|
||||
plps.push({ pt: new Vector2(w, 0), bul: 0 })
|
||||
plps.push({ pt: new Vector2(w, l), bul: 0 })
|
||||
plps.push({ pt: new Vector2(0, l), bul: 0 })
|
||||
let pls = new Polyline(plps)
|
||||
pls.CloseMark = true
|
||||
return pls
|
||||
}
|
||||
|
||||
/** 多段线,添加位置移动 返回新多段线 */
|
||||
static moveTo(pl: Polyline, x: number, y: number): Polyline {
|
||||
let lindData = pl.LineData
|
||||
let pos = pl.Position
|
||||
|
||||
let newPts: PolylineProps[] = []
|
||||
for (let p of lindData) {
|
||||
let nx = p.pt.x + pos.x + x
|
||||
let ny = p.pt.y + pos.y + y
|
||||
if (ny < 7.9) {
|
||||
// console.log('修边小于 7.9????', ny)
|
||||
}
|
||||
let bul = p.bul
|
||||
newPts.push({ pt: new Vector2(nx, ny), bul })
|
||||
}
|
||||
let npl = new Polyline(newPts)
|
||||
npl.CloseMark = pl.CloseMark
|
||||
return npl
|
||||
}
|
||||
|
||||
/** 重设 多段线的几点 */
|
||||
static resetPosition(pl: Polyline): Polyline {
|
||||
let lindData = pl.LineData
|
||||
let pos = pl.Position
|
||||
|
||||
let newPts: PolylineProps[] = []
|
||||
for (let p of lindData) {
|
||||
let nx = p.pt.x + pos.x
|
||||
let ny = p.pt.y + pos.y
|
||||
let bul = p.bul
|
||||
newPts.push({ pt: new Vector2(nx, ny), bul })
|
||||
}
|
||||
let npl = new Polyline(newPts)
|
||||
npl.CloseMark = pl.CloseMark
|
||||
return npl
|
||||
}
|
||||
|
||||
/** 获得v型刀走刀路径 */o
|
||||
static getVModelPoints(pl: Polyline, depth: number, ang: number): any[] {
|
||||
// let ang = Math.PI * (0.5 * angle) / 180 ;
|
||||
let ps:any[] = []
|
||||
let bx = pl.Position.x
|
||||
let by = pl.Position.y
|
||||
if (ang > 0.01) {
|
||||
let rt = VKnifToolPath(pl, depth, ang / 2)
|
||||
ps = rt.map((t) => { return { x: t.pt.x + bx, y: t.pt.y + by, z: t.pt.z, bul: t.bul, r: 0 } })
|
||||
}
|
||||
else {
|
||||
ps = pl.LineData.map((t) => { return { x: t.pt.x + bx, y: t.pt.y + by, z: 0, bul: t.bul, r: 0 } })
|
||||
}
|
||||
for (let i = 0; i < ps.length; i++) {
|
||||
let p = ps[i]
|
||||
if (p.bul == 0)
|
||||
continue
|
||||
let p2 = (i == ps.length - 1 ? ps[0] : ps[i + 1])
|
||||
let r = this.getArcRadius(p.x, p.y, p2.x, p2.y, p.bul)
|
||||
p.r = r
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
static ConverPolyLin2Circle(polyline: Polyline, fuzz = 0.1): Circle | undefined {
|
||||
let box = polyline.BoundingBox
|
||||
let size = box.getSize(new Vector3())
|
||||
if (!this.equaln(size.x, size.y, fuzz))// 盒子四方
|
||||
return undefined
|
||||
|
||||
let circleLength = 2 * Math.PI * size.x
|
||||
if (!this.equaln(circleLength, polyline.Length, fuzz * 2))
|
||||
return undefined
|
||||
|
||||
let circleArea = Math.PI * size.x * size.x
|
||||
if (!this.equaln(circleArea, polyline.Area, fuzz * 2))
|
||||
return undefined
|
||||
|
||||
let r = size.x// 必须备份(因为我们要复用这个vector变量)
|
||||
return new Circle(box.getCenter(size), r)
|
||||
}
|
||||
// 有问题
|
||||
static getVModelPoints_offset(pl: Polyline, offset: number, depth: number, angle: number) {
|
||||
let npl = offset == 0 ? pl : pl.GetOffsetCurves(offset)[0]
|
||||
|
||||
return PolylineHelper.getVModelPoints(npl, depth, angle)
|
||||
}
|
||||
|
||||
static getArcRadius(x1: number, y1: number, x2: number, y2: number, bul: number): number {
|
||||
let bul2 = Math.abs(bul)
|
||||
let d = Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) / 2
|
||||
return 0.5 * d * (1 + bul2 ** 2) / bul2
|
||||
}
|
||||
|
||||
// 圆 转 多段线
|
||||
static cicleToPolyline(c: Circle): Polyline | undefined {
|
||||
let arcs = c.GetSplitCurves([0, 0.5])
|
||||
let pl = Polyline.FastCombine(arcs)
|
||||
return pl
|
||||
}
|
||||
|
||||
/** 判断多段线是否 重叠 */
|
||||
static isIntersect(pl1: Polyline, pl2: Polyline): boolean {
|
||||
let box1 = this.getBox(pl1)
|
||||
let box2 = this.getBox(pl2)
|
||||
|
||||
if (!box1.intersectsBox(box2))
|
||||
return false // 肯定不相交
|
||||
|
||||
let ipts = pl1.IntersectWith(pl2, 0)
|
||||
if (ipts.length === 0) {
|
||||
if (pl1.Area > pl2.Area)// 缓存面积
|
||||
{
|
||||
if (isTargetCurInOrOnSourceCur(pl1, pl2))
|
||||
return true // 包含
|
||||
}
|
||||
else {
|
||||
if (isTargetCurInOrOnSourceCur(pl2, pl1))
|
||||
return true // 包含
|
||||
}
|
||||
return false
|
||||
}
|
||||
else {
|
||||
return true // 有交点 一定有交集
|
||||
}
|
||||
}
|
||||
|
||||
// 多段线 圆弧合并
|
||||
static mergeCurve(pl2: Polyline): Polyline|undefined {
|
||||
const curves = pl2.Explode()
|
||||
arrayRemoveDuplicateBySort(curves, (c1, c2) => {
|
||||
return c1.Join(c2) === Status.True
|
||||
})
|
||||
|
||||
return Polyline.FastCombine(curves)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 两片板的干涉检查
|
||||
*
|
||||
* @param pl1
|
||||
* @param pls1_inner
|
||||
* @param pls1_model
|
||||
* @param pl2
|
||||
* @param pls2_inner
|
||||
* @param pls2_model
|
||||
* @returns
|
||||
*/
|
||||
static isOverLap(pl1: Polyline, pls1_inner: Polyline[], pls1_model: Polyline[], pl2: Polyline, pls2_inner: Polyline[], pls2_model: Polyline[]) {
|
||||
// 是否干涉, 被包含在造型洞,不算干涉
|
||||
let isOverlap = this.boxIsOverlap(pl1, pls1_inner, pl2, pls2_inner)
|
||||
|
||||
if (isOverlap)
|
||||
return true
|
||||
|
||||
// 造型 ,2v 刀路 是否干涉
|
||||
for (let pl1_model of pls1_model) {
|
||||
if (pl1_model.IntersectWith(pl2, 0).length > 0)
|
||||
return true
|
||||
for (let pl2_inner of pls2_inner) {
|
||||
if (pl1_model.IntersectWith(pl2_inner, 0).length > 0)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for (let pl2_model of pls2_model) {
|
||||
if (pl2_model.IntersectWith(pl1, 0).length > 0)
|
||||
return true
|
||||
for (let pl1_inner of pls1_inner) {
|
||||
if (pl2_model.IntersectWith(pl1_inner, 0).length > 0)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static boxIsOverlap(pl1: Polyline, pls1_inner: Polyline[], pl2: Polyline, pls2_inner: Polyline[]) {
|
||||
let box1 = this.getBox(pl1)
|
||||
let box2 = this.getBox(pl2)
|
||||
|
||||
if (!box1.intersectsBox(box2))
|
||||
return false // 肯定不相交
|
||||
|
||||
let ipts = pl1.IntersectWith(pl2, 0)
|
||||
if (ipts.length > 0)
|
||||
return true // 有交点 一定有交集
|
||||
|
||||
if (pl1.Area > pl2.Area)// 缓存面积
|
||||
{
|
||||
if (isTargetCurInOrOnSourceCur(pl1, pl2)) // pl1包含 pl2
|
||||
{
|
||||
for (let mpl of pls1_inner) // 如果pl1有造型洞包含pl2, 则表示不干涉,返回false
|
||||
|
||||
{
|
||||
if (isTargetCurInOrOnSourceCur(mpl, pl2) == true)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isTargetCurInOrOnSourceCur(pl2, pl1)) // pl2包含 pl1
|
||||
{
|
||||
for (let mpl of pls2_inner) // 如果pl2有造型洞包含pl1, 则表示不干涉,返回false
|
||||
|
||||
{
|
||||
if (isTargetCurInOrOnSourceCur(mpl, pl1) == true)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** 判断 点是否在多段线内 */
|
||||
static isPointInPolyline(pl1: Polyline, x: number, y: number): boolean {
|
||||
return pl1.PtInCurve(new Vector3(x, y, 0))
|
||||
}
|
||||
|
||||
static getBox(pl1: Polyline): Box3 {
|
||||
if (!Reflect.has(pl1,'box_tp'))
|
||||
{
|
||||
Reflect.set(pl1,'box_tp',pl1.BoundingBox)
|
||||
}
|
||||
|
||||
return pl1['box_tp'] as Box3
|
||||
}
|
||||
|
||||
static getArea(pl1: Polyline): number {
|
||||
if (!Reflect.has(pl1,'area_tp'))
|
||||
{
|
||||
Reflect.set(pl1,'area_tp',pl1.Area)
|
||||
}
|
||||
|
||||
|
||||
return pl1['area_tp'] as number
|
||||
}
|
||||
|
||||
static getPath(pl: Polyline): Path2D {
|
||||
let path = new Path2D()
|
||||
let p0 = pl.LineData[0].pt
|
||||
path.moveTo(p0.x, p0.y)
|
||||
|
||||
for (let i = 0; i < pl.LineData.length; i++) {
|
||||
let p0 = pl.LineData[i].pt
|
||||
let p1 = (i == pl.LineData.length - 1) ? pl.LineData[0].pt : pl.LineData[i + 1].pt
|
||||
let bul = pl.LineData[i].bul
|
||||
if (bul == 0) {
|
||||
path.lineTo(p1.x, p1.y)
|
||||
}
|
||||
else {
|
||||
let arc = new Arc2d(new Point2d(p0.x, p0.y), new Point2d(p1.x, p1.y), bul)
|
||||
path.arc(arc.m_Center.m_X, arc.m_Center.m_Y, arc.m_Radius, arc.m_StartAngle, arc.m_EndAngle, bul < 0)
|
||||
}
|
||||
}
|
||||
path.closePath()
|
||||
return path
|
||||
}
|
||||
|
||||
static equaln(v1: number, v2: number, fuzz = 1e-5) {
|
||||
return Math.abs(v1 - v2) <= fuzz
|
||||
}
|
||||
|
||||
static toClipboard(en: Polyline | any) {
|
||||
let f = new CADFiler()
|
||||
f.Write(1)// 实体个数
|
||||
f.WriteObject(en)
|
||||
|
||||
copyTextToClipboard(f.ToString())
|
||||
}
|
||||
|
||||
static getStrPLs(ens: any[]) {
|
||||
if (ens.length == 0)
|
||||
return ''
|
||||
let f = new CADFiler()
|
||||
f.Write(ens.length)// 实体个数
|
||||
for (let en of ens)
|
||||
f.WriteObject(en)
|
||||
|
||||
return f.ToString()
|
||||
}
|
||||
}
|
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
|
||||
}
|
||||
}
|
85
tests/dev1/dataHandle/common/LayoutEngine/Simplify2.ts
Normal file
85
tests/dev1/dataHandle/common/LayoutEngine/Simplify2.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Vector2 } from '../Vector2'
|
||||
|
||||
interface P {
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface IOffset {
|
||||
negativeOffset: number
|
||||
positiveOffset: number
|
||||
}
|
||||
|
||||
/** 点p到线段P1P2 的最短距离的平方,线段不延伸 */
|
||||
function GetSqSegDist(p: P, p1: P, p2: P): number {
|
||||
let x = p1.x
|
||||
let y = p1.y
|
||||
let dx = p2.x - x
|
||||
let dy = p2.y - y
|
||||
|
||||
if (dx !== 0 || dy !== 0)// 不是0长度线
|
||||
{
|
||||
const t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy)
|
||||
if (t > 1) {
|
||||
x = p2.x
|
||||
y = p2.y
|
||||
}
|
||||
else if (t > 0) {
|
||||
x += dx * t
|
||||
y += dy * t
|
||||
}
|
||||
}
|
||||
dx = p.x - x
|
||||
dy = p.y - y
|
||||
return dx * dx + dy * dy
|
||||
}
|
||||
|
||||
function CrossVector2(a: P, b: P)
|
||||
{
|
||||
return a.x * b.y - a.y * b.x
|
||||
}
|
||||
|
||||
// Ramer-Douglas-Peucker algorithm
|
||||
function SimplifyDPStep(points: P[], first: number, last: number, sqTolerance: number, simplified: P[], offset: IOffset): void {
|
||||
let maxSqDist = 0
|
||||
let index: number
|
||||
const fp = points[first]
|
||||
const lp = points[last]
|
||||
|
||||
for (let i = first + 1; i < last; i++) {
|
||||
const p = points[i]
|
||||
const sqDist = GetSqSegDist(p, fp, lp)
|
||||
if (sqDist > maxSqDist) {
|
||||
index = i
|
||||
maxSqDist = sqDist
|
||||
}
|
||||
}
|
||||
|
||||
if (maxSqDist > sqTolerance) {
|
||||
if (index - first > 1)
|
||||
SimplifyDPStep(points, first, index, sqTolerance, simplified, offset)
|
||||
simplified.push(points[index])
|
||||
if (last - index > 1)
|
||||
SimplifyDPStep(points, index, last, sqTolerance, simplified, offset)
|
||||
}
|
||||
else {
|
||||
// 记录偏移
|
||||
const v = new Vector2(lp.x - fp.x, lp.y - fp.y).normalize()
|
||||
for (let i = first + 1; i < last; i++) {
|
||||
const p = points[i]
|
||||
const offsetDist = -CrossVector2(v, { x: p.x - fp.x, y: p.y - fp.y })
|
||||
offset.positiveOffset = Math.max(offset.positiveOffset, offsetDist)
|
||||
offset.negativeOffset = Math.min(offset.negativeOffset, offsetDist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ramer-Douglas-Peucker 算法
|
||||
export function SimplifyDouglasPeucker(points: P[], sqTolerance: number): [P[], IOffset] {
|
||||
const last = points.length - 1
|
||||
const simplified: P[] = [points[0]]
|
||||
const offset: IOffset = { negativeOffset: 0, positiveOffset: 0 }
|
||||
SimplifyDPStep(points, 0, last, sqTolerance, simplified, offset)
|
||||
simplified.push(points[last])
|
||||
return [simplified, offset]
|
||||
}
|
773
tests/dev1/dataHandle/common/LayoutEngine/SpacePlus.ts
Normal file
773
tests/dev1/dataHandle/common/LayoutEngine/SpacePlus.ts
Normal file
@@ -0,0 +1,773 @@
|
||||
import { PolylineHelper } from '../../common/LayoutEngine/PolylineHelper.js'
|
||||
import type { Curve2d } from '../../common/base/CAD.js'
|
||||
import { Arc2d, CADExt, Line2d, Point2d } from '../../common/base/CAD.js'
|
||||
import { equal } from '../../common/base/MathComm.js'
|
||||
import { PlaceSpace,PlaceBlock,TextureType } from '../../confClass.js'
|
||||
|
||||
/** 生成矩形空间的相关算法 */
|
||||
export class SpacePlus
|
||||
{
|
||||
/** 纹路:0正文 1反纹 */
|
||||
static texture :TextureType = 0 // = TextureType.NORMAL_TEXTURE // 正纹
|
||||
|
||||
/** 设置空间的方向,横向或竖向 */
|
||||
static setSpaceStyle(block: PlaceBlock)
|
||||
{
|
||||
|
||||
if (block.texture == TextureType.NORMAL_TEXTURE) // 正纹
|
||||
{
|
||||
this.texture = 0
|
||||
}
|
||||
else if (block.texture == TextureType.REVERSE_TEXTURE) // 反纹
|
||||
{
|
||||
this.texture = 1
|
||||
}
|
||||
else // 可翻转
|
||||
{
|
||||
if (block.cutWidth < block.cutLength)
|
||||
{
|
||||
this.texture = 0
|
||||
}
|
||||
else
|
||||
{
|
||||
this.texture = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 复制轮廓 */
|
||||
static copyBorder(curves: Curve2d[]): Curve2d[]
|
||||
{
|
||||
const newCurves: Curve2d[] = []
|
||||
for (let i = 0; i < curves.length; i++)
|
||||
{
|
||||
const curve = curves[i]
|
||||
const sp = curve.StartPoint
|
||||
const ep = curve.EndPoint
|
||||
if (curve instanceof Arc2d)
|
||||
{
|
||||
const arc = new Arc2d(new Point2d(sp.m_X, sp.m_Y), new Point2d(ep.m_X, ep.m_Y), curve.Bul)
|
||||
newCurves.push(arc)
|
||||
}
|
||||
else
|
||||
{
|
||||
// 后面会修改line里头的起点终点,所以必须重新初始化, 确保不能影响原来的轮廓 border .
|
||||
const newLine = new Line2d(new Point2d(sp.m_X, sp.m_Y), new Point2d(ep.m_X, ep.m_Y))
|
||||
newCurves.push(newLine)
|
||||
}
|
||||
}
|
||||
return newCurves
|
||||
}
|
||||
|
||||
/** 从轮廓中分析空间 */
|
||||
static borderToSpace(orgcurves: Curve2d[])
|
||||
{
|
||||
const curves = this.copyBorder(orgcurves)
|
||||
// 1.新建线段列表 不影响源数据
|
||||
// 弧线转成 直线
|
||||
const lines: Line2d[] = []
|
||||
for (let i = 0; i < curves.length;)
|
||||
{
|
||||
const curve = curves[i]
|
||||
const sp = curve.StartPoint
|
||||
const ep = curve.EndPoint
|
||||
if (curve instanceof Arc2d)
|
||||
{
|
||||
const len = Math.abs(curve.m_Radius * curve.m_AllAngle) // 圆弧长度
|
||||
|
||||
if (len > 200) // 圆弧长大于 200 则拆分
|
||||
{
|
||||
const count = Math.ceil(len / 100)
|
||||
const newArcs = CADExt.SplitArc(curve, count)
|
||||
curves.splice(i, 1)
|
||||
for (let j = newArcs.length - 1; j >= 0; j--)
|
||||
{
|
||||
curves.splice(i, 0, newArcs[j])
|
||||
}
|
||||
continue
|
||||
}
|
||||
else // 圆弧转换 直线
|
||||
{
|
||||
if (curve.Bul == 0)
|
||||
{
|
||||
curve.Parse()
|
||||
}
|
||||
const newLine = new Line2d(new Point2d(sp.m_X, sp.m_Y), new Point2d(ep.m_X, ep.m_Y))
|
||||
// 外凸 直接转
|
||||
if (curve.Bul >= 0)
|
||||
{
|
||||
newLine.canotSplit = true // 不能拆分
|
||||
lines.push(newLine)
|
||||
}
|
||||
else // 内凹 算要算一下 弧形外接 矩形面积
|
||||
{
|
||||
const pts:any[] = []
|
||||
pts.push({ x: curve.StartPoint.m_X, y: curve.StartPoint.m_Y, bul: curve.Bul })
|
||||
pts.push({ x: curve.EndPoint.m_X, y: curve.EndPoint.m_Y, bul: 0 })
|
||||
const pl = PolylineHelper.create(pts)
|
||||
const box = pl.BoundingBox
|
||||
this.setDirect(newLine)
|
||||
const fx = newLine.fx
|
||||
const newPoints :any[] = []
|
||||
newPoints.push({ x: sp.m_X, y: sp.m_Y })
|
||||
switch (fx)
|
||||
{
|
||||
case 0:
|
||||
newPoints.push({ x: box.min.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.min.y })
|
||||
break
|
||||
case 1:
|
||||
newPoints.push({ x: box.max.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.max.y })
|
||||
break
|
||||
case 2:
|
||||
newPoints.push({ x: box.max.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.max.y })
|
||||
break
|
||||
case 3:
|
||||
newPoints.push({ x: box.min.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.min.y })
|
||||
break
|
||||
case 4:
|
||||
newPoints.push({ x: box.min.x, y: sp.m_Y })
|
||||
newPoints.push({ x: box.min.x, y: box.max.y })
|
||||
newPoints.push({ x: ep.m_X, y: box.max.y })
|
||||
break
|
||||
case 5:
|
||||
newPoints.push({ x: sp.m_X, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: box.min.y })
|
||||
newPoints.push({ x: box.min.x, y: ep.m_Y })
|
||||
break
|
||||
case 6:
|
||||
newPoints.push({ x: box.max.x, y: sp.m_Y })
|
||||
newPoints.push({ x: box.max.x, y: box.min.y })
|
||||
newPoints.push({ x: ep.m_X, y: box.min.y })
|
||||
break
|
||||
case 7:
|
||||
newPoints.push({ x: sp.m_X, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: box.max.y })
|
||||
newPoints.push({ x: box.max.x, y: ep.m_Y })
|
||||
break
|
||||
default:
|
||||
}
|
||||
newPoints.push({ x: ep.m_X, y: ep.m_Y })
|
||||
for (let n = 0; n < newPoints.length - 1; n++)
|
||||
{
|
||||
const m = n + 1
|
||||
const newLine = new Line2d(new Point2d(newPoints[n].x, newPoints[n].y), new Point2d(newPoints[m].x, newPoints[m].y))
|
||||
lines.push(newLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 后面会修改line里头的起点终点,所以必须重新初始化 ,确保不能影响原来的轮廓 border .
|
||||
const newLine = new Line2d(new Point2d(sp.m_X, sp.m_Y), new Point2d(ep.m_X, ep.m_Y))
|
||||
lines.push(newLine)
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
// 2 轮廓需要够大
|
||||
let minX = 1000
|
||||
let minY = 1000
|
||||
let maxX = -1000
|
||||
let maxY = -1000
|
||||
for (const line of lines)
|
||||
{
|
||||
if (line.StartPoint.m_X < minX)
|
||||
minX = line.StartPoint.m_X
|
||||
if (line.StartPoint.m_X > maxX)
|
||||
maxX = line.StartPoint.m_X
|
||||
if (line.StartPoint.m_Y < minY)
|
||||
minY = line.StartPoint.m_Y
|
||||
if (line.StartPoint.m_Y > maxY)
|
||||
maxY = line.StartPoint.m_Y
|
||||
if (line.EndPoint.m_X < minX)
|
||||
minX = line.EndPoint.m_X
|
||||
if (line.EndPoint.m_X > maxX)
|
||||
maxX = line.EndPoint.m_X
|
||||
if (line.EndPoint.m_Y < minY)
|
||||
minY = line.EndPoint.m_Y
|
||||
if (line.EndPoint.m_Y > maxY)
|
||||
maxY = line.EndPoint.m_Y
|
||||
}
|
||||
if (maxX - minX < 50)
|
||||
return // 太小了,就不要分析
|
||||
if (maxY - minY < 50)
|
||||
return
|
||||
if ((maxX - minX) * (maxY - minY) < 10000)
|
||||
return []
|
||||
|
||||
// 3.将长斜线 ,转成 多段
|
||||
|
||||
for (let i = 0; i < lines.length;)
|
||||
{
|
||||
const line = lines[i]
|
||||
if (this.isXL(line))
|
||||
{
|
||||
if (line.canotSplit) // 不能拆分
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
const sp = line.StartPoint
|
||||
const ep = line.EndPoint
|
||||
if (line.m_Length > 300 && Math.abs(sp.m_X - ep.m_X) > 50 && Math.abs(sp.m_X - ep.m_X) > 50) // 够长,截断
|
||||
{
|
||||
const cx = (line.StartPoint.m_X + line.EndPoint.m_X) / 2
|
||||
const cy = (line.StartPoint.m_Y + line.EndPoint.m_Y) / 2
|
||||
|
||||
line.EndPoint = new Point2d(cx, cy)
|
||||
line.Parse()
|
||||
const newLine = new Line2d(new Point2d(cx, cy), new Point2d(ep.m_X, ep.m_Y))
|
||||
lines.splice(i + 1, 0, newLine)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// 3.斜线 -> 横平竖直
|
||||
for (let i = 0; i < lines.length; i++)
|
||||
{
|
||||
this.turnToLine(lines, i)
|
||||
}
|
||||
|
||||
// 6.将line 标识方向
|
||||
for (let i = lines.length - 1; i >= 0; i--)
|
||||
{
|
||||
if (this.setDirect(lines[i]) == -1)
|
||||
lines.splice(i, 1)
|
||||
}
|
||||
|
||||
const tmpSpaces = []
|
||||
// 7.开始分析空间
|
||||
this.parseSpace(lines, tmpSpaces)
|
||||
// 8.合并空间
|
||||
this.mergeSpaces(tmpSpaces)
|
||||
// 9 生成
|
||||
const spaces: PlaceSpace[] = []
|
||||
for (const tmp of tmpSpaces)
|
||||
{
|
||||
const space = PlaceSpace.create(tmp.x1, tmp.y1, tmp.x2, tmp.y2)
|
||||
spaces.push(space)
|
||||
}
|
||||
return spaces
|
||||
}
|
||||
|
||||
/** 闭合(逆时针)的线段内分析空间 */
|
||||
private static parseSpace(lines: Line2d[], spaces: any[])
|
||||
{
|
||||
const childs = this.resetLines(lines)
|
||||
if (childs.length > 1) // 分叉
|
||||
{
|
||||
for (const child of childs)
|
||||
{
|
||||
this.parseSpace(child, spaces)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 没有分支,才开始真正计算
|
||||
lines = childs[0]
|
||||
if (lines.length < 4)
|
||||
return // 至少有4条
|
||||
|
||||
/**
|
||||
* 思路:在线段里头 找到一段连续且成矩形的3条直线,即可组成一个矩形
|
||||
* 然后:去掉(或缩短)这3条直线,再重新找
|
||||
*/
|
||||
let space
|
||||
for (let i = 0; i < lines.length; i++)
|
||||
{
|
||||
space = this.createSpace(lines, i)
|
||||
if (space)
|
||||
break
|
||||
}
|
||||
// 没找到任何空间, 直接退出
|
||||
if (!space)
|
||||
return
|
||||
|
||||
// 找到了,继续往下找
|
||||
spaces.push(space)
|
||||
this.parseSpace(lines, spaces)
|
||||
}
|
||||
|
||||
/** 分析空间大的保留,小的合并 */
|
||||
static mergeSpaces(spaces: any[])
|
||||
{
|
||||
const minW = 50 // 空间最小宽度
|
||||
const minL = 200
|
||||
const avgW = 60
|
||||
|
||||
// 不损失 合并空间
|
||||
for (let i = 0; i < spaces.length;)
|
||||
{
|
||||
const space1 = spaces[i]
|
||||
let hasMerge = false // 是否合并一个空间
|
||||
for (let j = i + 1; j < spaces.length;)
|
||||
{
|
||||
const space2 = spaces[j]
|
||||
hasMerge = mergeSpace(space1, space2) // space2 是否被合并
|
||||
if (hasMerge)
|
||||
{
|
||||
spaces.splice(j, 1)
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
if (hasMerge)
|
||||
break
|
||||
i++
|
||||
}
|
||||
// 小空间 委屈合并
|
||||
for (let i = 0; i < spaces.length;)
|
||||
{
|
||||
const space1 = spaces[i]
|
||||
if (big(space1) == false) // 小空间,找别人合并
|
||||
{
|
||||
mergeSpace2(i)
|
||||
spaces.splice(i, 1)// 删除小空间
|
||||
continue
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
/** 空间 大否 */
|
||||
function big(space)
|
||||
{
|
||||
let w = space.x2 - space.x1
|
||||
if (w < minW)
|
||||
return false
|
||||
let l = space.y2 - space.y1
|
||||
if (l < minW)
|
||||
return false
|
||||
|
||||
if (w > l)
|
||||
[w, l] = [l, w]
|
||||
|
||||
if (w >= minW && l >= minL)
|
||||
return true
|
||||
if (w >= avgW && l >= avgW)
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** 不损失 合并空间 */
|
||||
function mergeSpace(sp1, sp2)
|
||||
{
|
||||
// 上下 宽一样
|
||||
if (equal(sp1.x1, sp2.x1) && equal(sp1.x2, sp2.x2) && (equal(sp1.y2, sp2.y1) || equal(sp2.y2, sp1.y1)))
|
||||
{
|
||||
sp1.y1 = Math.min(sp1.y1, sp2.y1)
|
||||
sp1.y2 = Math.max(sp1.y2, sp2.y2)
|
||||
return true
|
||||
}
|
||||
// 左右 高一样
|
||||
if (equal(sp1.y1, sp2.y1) && equal(sp1.y2, sp2.y2) && (equal(sp1.x2, sp2.x1) || equal(sp2.x2, sp1.x1)))
|
||||
{
|
||||
sp1.x1 = Math.min(sp1.x1, sp2.x1)
|
||||
sp1.x2 = Math.max(sp1.x2, sp2.x2)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** 损失 合并空间,四周找一个空间,合并后效果最好的 */
|
||||
function mergeSpace2(index: number)
|
||||
{
|
||||
const cur = spaces[index]
|
||||
const canSpaces = []
|
||||
for (let i = 0; i < spaces.length; i++)
|
||||
{
|
||||
if (i == index)
|
||||
continue
|
||||
const oth = spaces[i]
|
||||
let x1, y1, x2, y2
|
||||
// 右边的
|
||||
if (equal(cur.x2, oth.x1) && cur.y1 < oth.y2 && cur.y2 > oth.y1)
|
||||
{
|
||||
x1 = cur.x1
|
||||
y1 = Math.max(cur.y1, oth.y1)
|
||||
x2 = oth.x2
|
||||
y2 = Math.min(cur.y2, oth.y2)
|
||||
}
|
||||
// 左边的
|
||||
else if (equal(cur.x1, oth.x2) && cur.y1 < oth.y2 && cur.y2 > oth.y1)
|
||||
{
|
||||
x1 = oth.x1
|
||||
y1 = Math.max(cur.y1, oth.y1)
|
||||
x2 = cur.x2
|
||||
y2 = Math.min(cur.y2, oth.y2)
|
||||
}
|
||||
// 下边的
|
||||
else if (equal(cur.y1, oth.y2) && cur.x1 < oth.x2 && cur.x2 > oth.x1)
|
||||
{
|
||||
x1 = Math.max(cur.x1, oth.x1)
|
||||
y1 = oth.y1
|
||||
x2 = Math.min(cur.x2, oth.x2)
|
||||
y2 = cur.y2
|
||||
}
|
||||
// 上边的
|
||||
else if (equal(cur.y2, oth.y1) && cur.x1 < oth.x2 && cur.x2 > oth.x1)
|
||||
{
|
||||
x1 = Math.max(cur.x1, oth.x1)
|
||||
y1 = cur.y1
|
||||
x2 = Math.min(cur.x2, oth.x2)
|
||||
y2 = oth.y2
|
||||
}
|
||||
else
|
||||
{
|
||||
continue
|
||||
}
|
||||
|
||||
// oth 原来面积
|
||||
const size_org = (oth.x2 - oth.x1) * (oth.y2 - oth.y1)
|
||||
const size_new = (x2 - x1) * (y2 - y1)
|
||||
|
||||
const size_plus = size_new - size_org
|
||||
if (size_plus < 0)
|
||||
continue // 合并 面积更小,就不要合并
|
||||
|
||||
const space = { x1, y1, x2, y2 }
|
||||
canSpaces.push({ size_plus, space, i })
|
||||
}
|
||||
if (canSpaces.length == 0)
|
||||
return // 没有可以合并的
|
||||
|
||||
// 按增大面积 排序
|
||||
canSpaces.sort((a, b) => b.size_plus - a.size_plus)
|
||||
// 取效果最好的。
|
||||
const newSpace = canSpaces[0].space
|
||||
// 替换 oth ,
|
||||
spaces.splice(canSpaces[0].i, 1, newSpace)
|
||||
}
|
||||
}
|
||||
|
||||
/** 整理多段线 */
|
||||
private static resetLines(lines: Line2d[]): Line2d[][]
|
||||
{
|
||||
for (let i = 0; i < lines.length;)
|
||||
{
|
||||
const lp = lines[i]
|
||||
let n = i + 1
|
||||
if (n == lines.length)
|
||||
n = 0
|
||||
const ln = lines[n]
|
||||
if (lp.m_Length < 0.001) // 太短了,移除
|
||||
{
|
||||
lines.splice(i, 1)
|
||||
i = 0
|
||||
continue
|
||||
}
|
||||
|
||||
if (lp.fx == ln.fx) // 同向,
|
||||
{
|
||||
lp.EndPoint = ln.EndPoint
|
||||
lp.Parse()
|
||||
lines.splice(n, 1)
|
||||
i = 0
|
||||
continue
|
||||
}
|
||||
if (lp.fx % 2 == ln.fx % 2) // 反向
|
||||
{
|
||||
lp.EndPoint = ln.EndPoint
|
||||
lp.Parse()
|
||||
this.setDirect(lp)
|
||||
lines.splice(n, 1)
|
||||
if ((lp.fx == 0 && lp.EndPoint.m_X < lp.StartPoint.m_X)
|
||||
|| (lp.fx == 1 && lp.EndPoint.m_Y < lp.StartPoint.m_Y)
|
||||
|| (lp.fx == 2 && lp.EndPoint.m_X > lp.StartPoint.m_X)
|
||||
|| (lp.fx == 3 && lp.EndPoint.m_Y > lp.StartPoint.m_Y)) // 这很奇葩,该空间 不能分析,直接清除所有线段
|
||||
{
|
||||
lines.splice(0, lines.length)
|
||||
return
|
||||
}
|
||||
i = 0
|
||||
continue
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// 判断 有向下的线,与向上的线 重叠。 则要分开; 类似 与 管 的下面 2个口
|
||||
let line0: Line2d
|
||||
let line1: Line2d
|
||||
let i = -1
|
||||
let j = -1
|
||||
let x = 0
|
||||
for (i = 0; i < lines.length; i++)
|
||||
{
|
||||
if (lines[i].fx == 3)
|
||||
{
|
||||
x = lines[i].StartPoint.m_X
|
||||
j = lines.findIndex(t => t.fx == 1 && t.StartPoint.m_X == x)
|
||||
if (j >= 0)
|
||||
{
|
||||
line0 = lines[i]
|
||||
line1 = lines[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!line0 || !line1)
|
||||
return [lines] // 没找到
|
||||
|
||||
const rt = []
|
||||
const si = Math.min(i, j)
|
||||
const ei = Math.max(i, j)
|
||||
|
||||
const lines_s: Line2d[] = []
|
||||
const lines_x: Line2d[] = []
|
||||
for (let i = si + 1; i <= ei - 1; i++)
|
||||
{
|
||||
lines_s.push(lines[i])
|
||||
}
|
||||
for (let i = ei + 1; i <= lines.length + si - 1; i++)
|
||||
{
|
||||
const ri = i % lines.length
|
||||
lines_x.push(lines[ri])
|
||||
}
|
||||
|
||||
if (lines_s.length >= 3)
|
||||
{
|
||||
const pe = lines_s[lines_s.length - 1].EndPoint
|
||||
const ps = lines_s[0].StartPoint
|
||||
const newLine = new Line2d(new Point2d(pe.m_X, pe.m_Y), new Point2d(ps.m_X, ps.m_Y))
|
||||
this.setDirect(newLine)
|
||||
lines_s.push(newLine)
|
||||
rt.push(lines_s)
|
||||
}
|
||||
if (lines_x.length >= 3)
|
||||
{
|
||||
const pe = lines_x[lines_x.length - 1].EndPoint
|
||||
const ps = lines_x[0].StartPoint
|
||||
const newLine = new Line2d(new Point2d(pe.m_X, pe.m_Y), new Point2d(ps.m_X, ps.m_Y))
|
||||
this.setDirect(newLine)
|
||||
lines_x.push(newLine)
|
||||
rt.push(lines_x)
|
||||
}
|
||||
|
||||
return rt
|
||||
}
|
||||
|
||||
/** line如果是斜线,转换成直线 */
|
||||
private static turnToLine(lines: Line2d[], i: number)
|
||||
{
|
||||
if (!this.isXL(lines[i]))
|
||||
return
|
||||
|
||||
const sp = lines[i].StartPoint
|
||||
const ep = lines[i].EndPoint
|
||||
const sx = sp.m_X
|
||||
const sy = sp.m_Y
|
||||
const ex = ep.m_X
|
||||
const ey = ep.m_Y
|
||||
let cx = 0
|
||||
let cy = 0
|
||||
// ↖ 换成 ←↑
|
||||
if (ex < sx && ey > sy)
|
||||
{
|
||||
cx = ex
|
||||
cy = sy
|
||||
}
|
||||
else if (ex < sx && ey < sy) // ↙ 换成 ←↓
|
||||
{
|
||||
cx = sx
|
||||
cy = ey
|
||||
}
|
||||
else if (ex > sx && ey < sy) // ↘ 换成 →↓
|
||||
{
|
||||
cx = ex
|
||||
cy = sy
|
||||
}
|
||||
else if (ex > sx && ey > sy) // ↗ 换成 →↑
|
||||
{
|
||||
cx = sx
|
||||
cy = ey
|
||||
}
|
||||
if (cx == 0 && cy == 0)
|
||||
return
|
||||
|
||||
const line1 = new Line2d(new Point2d(sx, sy), new Point2d(cx, cy))
|
||||
const line2 = new Line2d(new Point2d(cx, cy), new Point2d(ex, ey))
|
||||
lines.splice(i, 1, line1, line2)
|
||||
}
|
||||
|
||||
/** 线段设置方向 */
|
||||
private static setDirect(line: Line2d)
|
||||
{
|
||||
let fx = -1 // 向右 0,向上 1,向左 2,向下 3 ,右上 4,左上5,左下6,右下 7
|
||||
if (line.EndPoint.m_X > line.StartPoint.m_X && equal(line.EndPoint.m_Y, line.StartPoint.m_Y))
|
||||
{
|
||||
fx = 0
|
||||
}
|
||||
else if (line.EndPoint.m_X < line.StartPoint.m_X && equal(line.EndPoint.m_Y, line.StartPoint.m_Y))
|
||||
{
|
||||
fx = 2
|
||||
}
|
||||
else if (line.EndPoint.m_Y > line.StartPoint.m_Y && equal(line.EndPoint.m_X, line.StartPoint.m_X))
|
||||
{
|
||||
fx = 1
|
||||
}
|
||||
else if (line.EndPoint.m_Y < line.StartPoint.m_Y && equal(line.EndPoint.m_X, line.StartPoint.m_X))
|
||||
{
|
||||
fx = 3
|
||||
}
|
||||
else if (line.EndPoint.m_X > line.StartPoint.m_X && line.EndPoint.m_Y > line.StartPoint.m_Y)
|
||||
{
|
||||
fx = 4
|
||||
}
|
||||
else if (line.EndPoint.m_X < line.StartPoint.m_X && line.EndPoint.m_Y > line.StartPoint.m_Y)
|
||||
{
|
||||
fx = 5
|
||||
}
|
||||
else if (line.EndPoint.m_X < line.StartPoint.m_X && line.EndPoint.m_Y < line.StartPoint.m_Y)
|
||||
{
|
||||
fx = 6
|
||||
}
|
||||
else if (line.EndPoint.m_X > line.StartPoint.m_X && line.EndPoint.m_Y < line.StartPoint.m_Y)
|
||||
{
|
||||
fx = 7
|
||||
}
|
||||
line.fx = fx
|
||||
return fx
|
||||
}
|
||||
|
||||
/** 斜线 */
|
||||
private static isXL(line: Curve2d) { return !equal(line.StartPoint.m_X, line.EndPoint.m_X) && !equal(line.StartPoint.m_Y, line.EndPoint.m_Y) }
|
||||
|
||||
/** 3条线,方向持续, 组成空间 */
|
||||
private static createSpace(lines: Line2d[], i: number)
|
||||
{
|
||||
let j = i + 1
|
||||
let n = i + 2
|
||||
if (j >= lines.length)
|
||||
j = j - lines.length
|
||||
if (n >= lines.length)
|
||||
n = n - lines.length
|
||||
|
||||
const line1 = lines[i]
|
||||
const line2 = lines[j]
|
||||
const line3 = lines[n]
|
||||
|
||||
const fx1 = line1.fx
|
||||
if (line2.fx != (fx1 + 1) % 4)
|
||||
return
|
||||
if (line3.fx != (fx1 + 2) % 4)
|
||||
return
|
||||
|
||||
// 安装板的纹路,进行开始计算空间,横的,还是竖的
|
||||
if (fx1 % 2 != this.texture)
|
||||
return
|
||||
|
||||
let x1, y1, x2, y2
|
||||
let sp, ep
|
||||
if (fx1 == 0)
|
||||
{
|
||||
x1 = Math.max(line1.StartPoint.m_X, line3.EndPoint.m_X)
|
||||
y1 = line2.StartPoint.m_Y
|
||||
x2 = line2.EndPoint.m_X
|
||||
y2 = line2.EndPoint.m_Y
|
||||
|
||||
sp = new Point2d(x1, y1)
|
||||
ep = new Point2d(x1, y2)
|
||||
}
|
||||
else if (fx1 == 1)
|
||||
{
|
||||
x1 = line2.EndPoint.m_X
|
||||
y1 = Math.max(line1.StartPoint.m_Y, line3.EndPoint.m_Y)
|
||||
x2 = line2.StartPoint.m_X
|
||||
y2 = line2.StartPoint.m_Y
|
||||
sp = new Point2d(x2, y1)
|
||||
ep = new Point2d(x1, y1)
|
||||
}
|
||||
else if (fx1 == 2)
|
||||
{
|
||||
x1 = line2.EndPoint.m_X
|
||||
y1 = line2.EndPoint.m_Y
|
||||
x2 = Math.min(line1.StartPoint.m_X, line3.EndPoint.m_X)
|
||||
y2 = line2.StartPoint.m_Y
|
||||
|
||||
sp = new Point2d(x2, y2)
|
||||
ep = new Point2d(x2, y1)
|
||||
}
|
||||
else if (fx1 == 3)
|
||||
{
|
||||
x1 = line2.StartPoint.m_X
|
||||
y1 = line2.StartPoint.m_Y
|
||||
x2 = line2.EndPoint.m_X
|
||||
y2 = Math.min(line1.StartPoint.m_Y, line3.EndPoint.m_Y)
|
||||
|
||||
sp = new Point2d(x1, y2)
|
||||
ep = new Point2d(x2, y2)
|
||||
}
|
||||
else
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
// 在新空间内,不能出现其他的线段,
|
||||
for (let o = 0; o < lines.length; o++)
|
||||
{
|
||||
if (o == i || o == j || o == n)
|
||||
continue
|
||||
const oth = lines[o]
|
||||
if (oth.EndPoint.m_X > x1 + 0.001 && oth.EndPoint.m_X < x2 - 0.001 && oth.EndPoint.m_Y > y1 + 0.001 && oth.EndPoint.m_Y < y2 - 0.001)
|
||||
return
|
||||
}
|
||||
|
||||
// 缩短line1, line3
|
||||
line1.EndPoint = sp
|
||||
line1.Parse()
|
||||
this.setDirect(line1)
|
||||
line3.StartPoint = ep
|
||||
line3.Parse()
|
||||
this.setDirect(line3)
|
||||
// 删除 line2 ,添加 新连接线
|
||||
const newline = new Line2d(new Point2d(sp.m_X, sp.m_Y), new Point2d(ep.m_X, ep.m_Y))
|
||||
this.setDirect(newline)
|
||||
|
||||
lines.splice(j, 1, newline)
|
||||
const space = { x1, y1, x2, y2 }
|
||||
return space
|
||||
}
|
||||
|
||||
/** 反转 */
|
||||
static reverseCurves(curves: Curve2d[]): Curve2d[]
|
||||
{
|
||||
const newCurs: Curve2d[] = []
|
||||
for (let i = curves.length - 1; i >= 0; i--)
|
||||
{
|
||||
const cur = curves[i]
|
||||
const sp = cur.StartPoint
|
||||
const ep = cur.EndPoint
|
||||
if (cur instanceof Arc2d)
|
||||
{
|
||||
const newArc = new Arc2d(ep, sp, -cur.Bul)
|
||||
newCurs.push(newArc)
|
||||
}
|
||||
else
|
||||
{
|
||||
const newline = new Line2d(ep, sp)
|
||||
newCurs.push(newline)
|
||||
}
|
||||
}
|
||||
return newCurs
|
||||
}
|
||||
}
|
104
tests/dev1/dataHandle/common/LayoutEngine/writeP.ts
Normal file
104
tests/dev1/dataHandle/common/LayoutEngine/writeP.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import type { PolylineProps } from 'cadapi'
|
||||
import { CADFiler, Polyline } from 'cadapi'
|
||||
import { Vector2 } from 'three'
|
||||
import { copyTextToClipboard } from '../base/CAD'
|
||||
|
||||
export class ClipboardTest {
|
||||
public static writePolyline1(pl: Polyline, pts1) {
|
||||
// pl 原图
|
||||
// pts :偏移后的点
|
||||
|
||||
const lined: PolylineProps[] = []
|
||||
const count = pts1.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
const p0 = pts1[i]
|
||||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: 0 })
|
||||
}
|
||||
const pl1 = new Polyline(lined)
|
||||
pl1.CloseMark = true
|
||||
|
||||
const f = new CADFiler()
|
||||
f.Clear()
|
||||
f.Write(2)
|
||||
f.WriteObject(pl)
|
||||
f.WriteObject(pl1)
|
||||
|
||||
const test = JSON.stringify(f.Data)
|
||||
|
||||
// for (let pl of lined)
|
||||
// f.WriteObject(pl)
|
||||
copyTextToClipboard(test)
|
||||
}
|
||||
|
||||
public static writePolyline2(pl: Polyline, pts1, pts2) {
|
||||
// pl 原图
|
||||
// pts :偏移后的点
|
||||
|
||||
const lined: PolylineProps[] = []
|
||||
const count = pts1.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
const p0 = pts1[i]
|
||||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: 0 })
|
||||
}
|
||||
const pl1 = new Polyline(lined)
|
||||
|
||||
const lined2: PolylineProps[] = []
|
||||
const count2 = pts2.length
|
||||
for (let i = 0; i < count2; i++) {
|
||||
const p0 = pts2[i]
|
||||
lined2.push({ pt: new Vector2(p0.x, p0.y), bul: 0 })
|
||||
}
|
||||
const pl2 = new Polyline(lined2)
|
||||
|
||||
const f = new CADFiler()
|
||||
f.Clear()
|
||||
f.Write(3)
|
||||
f.WriteObject(pl)
|
||||
f.WriteObject(pl1)
|
||||
f.WriteObject(pl2)
|
||||
const test = JSON.stringify(f.Data)
|
||||
|
||||
// for (let pl of lined)
|
||||
// f.WriteObject(pl)
|
||||
copyTextToClipboard(test)
|
||||
}
|
||||
|
||||
public static writeClipboard(pts) {
|
||||
const lined: PolylineProps[] = []
|
||||
const count = pts.length
|
||||
for (let i = 0; i < count; i++) {
|
||||
const p0 = pts[i]
|
||||
lined.push({ pt: new Vector2(p0.x, p0.y), bul: 0 })
|
||||
}
|
||||
|
||||
const pls = new Polyline(lined)
|
||||
const f = new CADFiler()
|
||||
f.Clear()
|
||||
f.Write(1)
|
||||
f.WriteObject(pls)
|
||||
const test = JSON.stringify(f.Data)
|
||||
|
||||
// for (let pl of lined)
|
||||
// f.WriteObject(pl)
|
||||
copyTextToClipboard(test)
|
||||
}
|
||||
|
||||
public static writePolyLine(pls) {
|
||||
const f = new CADFiler()
|
||||
f.Clear()
|
||||
f.Write(1)
|
||||
f.WriteObject(pls)
|
||||
const test = JSON.stringify(f.Data)
|
||||
copyTextToClipboard(test)
|
||||
}
|
||||
|
||||
public static write2PolyLine(pls, pls2) {
|
||||
const f = new CADFiler()
|
||||
f.Clear()
|
||||
f.Write(2)
|
||||
f.WriteObject(pls)
|
||||
f.WriteObject(pls2)
|
||||
const test = JSON.stringify(f.Data)
|
||||
copyTextToClipboard(test)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user