Files
cut-abstractions/tests/dev1/dataHandle/common/core/Part.ts

395 lines
11 KiB
TypeScript
Raw Normal View History

2025-07-22 18:22:31 +08:00
import type { Box2 } from '../Box2'
import type { NestFiler } from '../Filer'
import type { Point } from '../Vector2'
import { RandomIndex } from '../Random'
import { FixIndex } from '../Util'
import { Vector2 } from '../Vector2'
import { GNestConfig } from './GNestConfig'
import { NestCache } from './NestCache'
import { PartState } from './PartState'
import { Path } from './Path'
import { PathGeneratorSingle } from './PathGenerator'
const EmptyArray = []
/**
*
*
*
*
*
* 0P
*
*
*
*
*
* 0
*
*
* 0
*
*
*
*
*/
export class Part<T = any, Matrix = any>
{
Id: number// 用于确定Part的唯一性,并且有助于在其他Work中还原
private _Holes: PartState[] = []
_RotateHoles: PartState[][] = []
// 零件的不同旋转状态,这个数组会在所有的状态间共享,以便快速切换状态
StateIndex = 0 // 旋转状态
RotatedStates: PartState[] = []// 可能的旋转状态列表
PlacePosition: Point // 放置位置(相对于容器的位置)
HolePosition: Point//
// #region 临时数据(不会被序列化到优化线程)
UserData: T// 只用于还原零件的显示状态(或者关联到实际数据)
Parent: Part// 如果这个零件放置在了网洞中,这个Parent表示这个网洞所属的零件,我们可以得到这个零件的放置信息并且可以从Container中的ParentM得到网洞相对于零件的位置
PlaceCS: Matrix// 放置矩阵 Matrix4
PlaceIndex: number// 放置的大板索引
// #endregion
GroupMap: { [key: number]: Part[] } = {}
get State(): PartState // 零件当前的状态
{
return this.RotatedStates[this.StateIndex]
}
get Holes(): PartState[]
{
if (GNestConfig.RotateHole)
return this._RotateHoles[this.StateIndex] || EmptyArray
return this._Holes
}
// 初始化零件的各个状态,按360度旋转个数
Init(path: Path, bin: Path, rotateCount = 4): this
{
let rotations: number[] = []
let a = 2 * Math.PI / rotateCount
for (let i = 0; i < rotateCount; i++)
{
rotations.push(a * i)
}
this.Init2(path, bin, rotations)
return this
}
// 初始化零件的各个状态,按旋转角度表
Init2(path: Path, bin: Path, rotations: number[] = []): this
{
let pathP = path.OrigionMinPoint
let path_0 = PathGeneratorSingle.Allocate(path)
let pathSet = new Set<Path>()
// 初始化零件的状态集合
for (let pa of rotations)
{
let partState = new PartState()
partState.Rotation = pa
if (pa === 0)
{
partState.Contour = path_0
partState.OrigionMinPoint = pathP
partState.MinPoint = path.OrigionMinPoint
}
else
{
let path_r = new Path(path.Points, pa)
partState.Contour = PathGeneratorSingle.Allocate(path_r)
partState.Contour.Area = path_0.Area
// 避免重复的Path进入State
if (pathSet.has(partState.Contour))
continue
let p0 = path_r.OrigionMinPoint
let c = Math.cos(-pa)
let s = Math.sin(-pa)
let x1 = p0.x * c - p0.y * s
let y1 = p0.x * s + p0.y * c
partState.OrigionMinPoint = new Vector2(pathP.x + x1, pathP.y + y1)
// 计算正确的最小点
let tempPath = new Path(path.OrigionPoints, pa)
partState.MinPoint = tempPath.OrigionMinPoint
}
// 记录已有Path
pathSet.add(partState.Contour)
// 必须能放置
if (bin.GetInsideNFP(partState.Contour))
{
this.RotatedStates.push(partState)
PathGeneratorSingle.RegisterId(partState.Contour)
}
}
// 为了复用NFP,不管第0个Path是否可用,都注册它.
if (this.RotatedStates.length > 4)
PathGeneratorSingle.RegisterId(path_0)
return this
}
ParseGroup(partOther: Part, bin: Path): Part[]
{
let arr = this.GroupMap[partOther.Id]
if (arr)
return arr
arr = []
if (this.Holes.length || partOther.Holes.length)
return arr
this.GroupMap[partOther.Id] = arr
if (this.State.Contour.IsRect || partOther.State.Contour.IsRect)
return arr
if (this.State.Contour.Area > this.State.Contour.BoundingBox.area * 0.9)
return arr
if (partOther.State.Contour.Area > partOther.State.Contour.BoundingBox.area * 0.9)
return arr
for (let i = 0; i < this.RotatedStates.length; i++)
{
let s1 = this.RotatedStates[i]
for (let j = 1; j < partOther.RotatedStates.length; j++)
{
let s2 = partOther.RotatedStates[j]
let nfps = s1.Contour.GetOutsideNFP(s2.Contour)
for (let nfp of nfps)
{
for (let k = 0; k < nfp.length * 2; k++)
{
let p: Point
if (k % 2 === 0)
{
p = { ...nfp[k / 2] }
}
else
{
let p1 = nfp[FixIndex(k / 2 - 0.5, nfp)]
let p2 = nfp[FixIndex(k / 2 + 0.5, nfp)]
p = { x: p1.x + p2.x, y: p1.y + p2.y }
p.x *= 0.5
p.y *= 0.5
}
p.x *= 1e-4
p.y *= 1e-4
let newBox = s2.Contour.BoundingBox.clone().translate(p)
newBox.union(s1.Contour.BoundingBox)
if (newBox.area < (s1.Contour.Area + s2.Contour.Area) * 1.3)
{
let partGroup = new PartGroup(this, partOther, i, j, p, newBox, bin)
if (partGroup.RotatedStates.length > 0
&& !arr.some(p => p.State.Contour === partGroup.State.Contour)// 类似的
)
{
arr.push(partGroup)
return arr
}
}
}
}
}
}
return arr
}
// 添加网洞
AppendHole(path: Path)
{
let hole = new PartState()
hole.Contour = PathGeneratorSingle.Allocate(path)
PathGeneratorSingle.RegisterId(hole.Contour)
hole.OrigionMinPoint = path.OrigionMinPoint
hole.Rotation = 0
this._Holes.push(hole)
if (GNestConfig.RotateHole)
for (let i = 0; i < this.RotatedStates.length; i++)
{
let r = this.RotatedStates[i].Rotation
let arr = this._RotateHoles[i]
if (!arr)
{
arr = []
this._RotateHoles[i] = arr
}
if (r === 0)
{
hole.MinPoint = path.OrigionMinPoint
arr.push(hole)
}
else
{
let newPath = new Path(path.Points, r)
let newHole = new PartState()
newHole.Rotation = r
newHole.Contour = PathGeneratorSingle.Allocate(newPath)
PathGeneratorSingle.RegisterId(newHole.Contour)
newHole.OrigionMinPoint = newPath.OrigionMinPoint
// 计算正确的最小点
let tempPath = new Path(path.OrigionPoints, r)
newHole.MinPoint = tempPath.OrigionMinPoint
arr.push(newHole)
}
}
}
// TODO:因为现在实现的是左右翻转,所以会出现角度匹配不完全的问题(缺失上下翻转)
Mirror(doubleFace: boolean)
{
let states = this.RotatedStates
let holes = this._Holes
let roholes = this._RotateHoles
if (!doubleFace)
{
this.RotatedStates = []
this._Holes = []
this._RotateHoles = []
}
let count = states.length
for (let i = 0; i < count; i++)
{
let s = states[i]
let ns = s.Mirror()
if (ns && !this.RotatedStates.some(state => state.Contour === s.Contour))
{
this.RotatedStates.push(ns)
if (this._Holes.length > 0)
this._Holes.push(holes[i].Mirror())
if (this._RotateHoles.length > 0)
this._RotateHoles.push(roholes[i].map(s => s.Mirror()))
}
}
}
// 浅克隆
Clone()
{
let part = new Part()
part.Id = this.Id
part.UserData = this.UserData
part.RotatedStates = this.RotatedStates
part.StateIndex = this.StateIndex
part._Holes = this._Holes
part._RotateHoles = this._RotateHoles
return part
}
// 旋转起来,改变自身旋转状态(变异)
Mutate(): this
{
this.StateIndex = RandomIndex(this.RotatedStates.length, this.StateIndex)
return this
}
// #region -------------------------File-------------------------
ReadFile(file: NestFiler)
{
this.Id = file.Read()
let count = file.Read() as number
this.RotatedStates = []
for (let i = 0; i < count; i++)
{
let state = new PartState()
state.ReadFile(file)
this.RotatedStates.push(state)
}
// 无旋转网洞
count = file.Read()
this._Holes = []
for (let i = 0; i < count; i++)
{
let state = new PartState()
state.ReadFile(file)
this._Holes.push(state)
}
// 旋转网洞
count = file.Read()
this._RotateHoles = []
for (let i = 0; i < count; i++)
{
let count2 = file.Read() as number
let holes: PartState[] = []
for (let j = 0; j < count2; j++)
{
let state = new PartState()
state.ReadFile(file)
holes.push(state)
}
this._RotateHoles.push(holes)
}
}
WriteFile(file: NestFiler)
{
file.Write(this.Id)
file.Write(this.RotatedStates.length)
for (let state of this.RotatedStates)
state.WriteFile(file)
// 非旋转网洞
file.Write(this._Holes.length)
for (let hole of this._Holes)
hole.WriteFile(file)
// 写入旋转网洞
file.Write(this._RotateHoles.length)
for (let holes of this._RotateHoles)
{
file.Write(holes.length)
for (let hole of holes)
{
hole.WriteFile(file)
}
}
}
// #endregion
}
// 零件组合
export class PartGroup extends Part
{
constructor(public part1: Part,
public part2: Part,
public index1: number,
public index2: number,
public p: Point,
public box: Box2,
bin: Path,
)
{
super()
let size = box.getSize(new Vector2())
this.Init2(NestCache.CreatePath(size.x, size.y, 0), bin, [0])
}
Export(): Part[]
{
this.part1.StateIndex = this.index1
this.part2.StateIndex = this.index2
this.part1.PlacePosition = {
x: this.PlacePosition.x - this.box.min.x * 1e4,
y: this.PlacePosition.y - this.box.min.y * 1e4,
}
this.part2.PlacePosition = {
x: this.PlacePosition.x - this.box.min.x * 1e4 + this.p.x * 1e4,
y: this.PlacePosition.y - this.box.min.y * 1e4 + this.p.y * 1e4,
}
return [this.part1, this.part2]
}
}