feat:提交
This commit is contained in:
394
tests/dev1/dataHandle/common/core/Part.ts
Normal file
394
tests/dev1/dataHandle/common/core/Part.ts
Normal file
@@ -0,0 +1,394 @@
|
||||
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 = []
|
||||
|
||||
/**
|
||||
* 零件类
|
||||
* 零件类可以绑定数据,也存在位置和旋转状态的信息
|
||||
*
|
||||
* 初始化零件:
|
||||
* 传入零件的轮廓,刀半径,包围容器(或者为空?)
|
||||
* 初始化用于放置的轮廓。将轮廓首点移动到0点,记录移动的点P。
|
||||
*
|
||||
* 零件放置位置:
|
||||
* 表示零件轮廓首点的位置。
|
||||
*
|
||||
* 零件的旋转:
|
||||
* 表示零件轮廓按照首点(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]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user