164 lines
4.5 KiB
TypeScript
164 lines
4.5 KiB
TypeScript
import { arrayRemoveIf } from '../ArrayExt'
|
|
import { clipperCpp } from '../ClipperCpp'
|
|
import { Sleep } from '../Sleep'
|
|
import { Individual } from './Individual'
|
|
import { NestCache } from './NestCache'
|
|
import { DefaultComparePointKeys } from './NestDatabase'
|
|
import type { Part } from './Part'
|
|
import type { Path } from './Path'
|
|
|
|
/**
|
|
* 优化器
|
|
* 配置优化器
|
|
* 放入零件
|
|
* 按下启动
|
|
* 按下暂停
|
|
* 清理机台
|
|
*/
|
|
export class OptimizeMachine {
|
|
// 配置
|
|
Config: {
|
|
PopulationCount: number// 种群个数
|
|
}
|
|
|
|
Bin: Path // 默认的容器
|
|
OddmentsBins: Path[]// 余料容器列表
|
|
Parts: Part[] // 所有的零件
|
|
ComparePointKeys: string[] = DefaultComparePointKeys// 用来决定零件靠边模式
|
|
|
|
// //计算重复的零件 TODO:需要对相同的零件提取出来
|
|
// PartCount: number[] = [];
|
|
|
|
private _IsSuspend = false
|
|
|
|
protected _Individuals: Individual[]// 个体列表
|
|
|
|
constructor() {
|
|
this.Config = { PopulationCount: 50 }
|
|
}
|
|
|
|
// 放入零件
|
|
PutParts(parts: Part[]) {
|
|
if (globalThis.document)
|
|
parts = parts.slice()
|
|
arrayRemoveIf(parts, p => p.RotatedStates.length === 0)
|
|
this.Parts = parts
|
|
|
|
// //计算重复的零件(暂时不用)
|
|
// for (let part of parts)
|
|
// {
|
|
// let count = this.PartCount[part.Id];
|
|
// this.PartCount[part.Id] = count === undefined ? 1 : (count + 1);
|
|
// }
|
|
}
|
|
|
|
callBack: (i: Individual) => Promise<void>
|
|
|
|
// 启动
|
|
async Start() {
|
|
if (this.Parts.length === 0)
|
|
return
|
|
console.log(this.Parts.length)
|
|
this._IsSuspend = false
|
|
NestCache.Clear()
|
|
this.Parts.sort((p1, p2) => p2.State.Contour.Area - p1.State.Contour.Area)
|
|
this._Individuals = [new Individual(this.Parts, 0.8, this.Bin, this.OddmentsBins, this.ComparePointKeys)]
|
|
for (let i = 1; i < this.Config.PopulationCount; i++) {
|
|
const parts = this.Parts.map(p => p.Clone())
|
|
if (i < 3) {
|
|
for (let i = parts.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[parts[i], parts[j]] = [parts[j], parts[i]]
|
|
parts[i].Mutate()
|
|
}
|
|
}
|
|
this._Individuals.push(new Individual(parts, 0.8, this.Bin, this.OddmentsBins, this.ComparePointKeys))
|
|
}
|
|
// 2.执行
|
|
await this.Run()
|
|
}
|
|
|
|
calcCount = 0
|
|
best = Number.POSITIVE_INFINITY
|
|
bestP: Individual
|
|
bestCount = 0
|
|
private async Run() {
|
|
console.time('1')
|
|
if (this.Parts.length === 0)
|
|
return
|
|
// 开始自然选择
|
|
while (!this._IsSuspend) // 实验停止信号
|
|
{
|
|
const goBack = this.calcCount - this.bestCount > 8000
|
|
// 1.适应环境(放置零件)
|
|
for (let i = 0; i < this._Individuals.length; i++) {
|
|
if (globalThis.document || !clipperCpp.lib)
|
|
await Sleep(0)
|
|
const p = this._Individuals[i]
|
|
if (this.calcCount < 1000 || this.calcCount % 1000 === 0)
|
|
p.Evaluate(this.best, true, i % 3)
|
|
else
|
|
p.Evaluate(this.best)
|
|
|
|
if (!this.bestP || p.Fitness < this.bestP.Fitness) {
|
|
this.bestP = p
|
|
this.best = p.Fitness
|
|
await this.callBack(p)
|
|
}
|
|
if (goBack)
|
|
p.mutationRate = 0.5
|
|
}
|
|
|
|
this.calcCount += this._Individuals.length
|
|
if (this.calcCount % 100 === 0) {
|
|
await Sleep(0)
|
|
}
|
|
|
|
this._Individuals.sort((i1, i2) => i1.Fitness - i2.Fitness)
|
|
const bestP = this._Individuals[0]
|
|
// //回调最好的
|
|
// if (bestP.Fitness < this.best)
|
|
// {
|
|
// this.best = bestP.Fitness;
|
|
// this.bestP = bestP;
|
|
// // console.timeLog("1", this.best, this.calcCount, NestCache.count1, NestCache.count2);
|
|
// if (this.callBack)
|
|
// await this.callBack(bestP);
|
|
// }
|
|
|
|
// 自然选择
|
|
this._Individuals.splice(-10)// 杀死它
|
|
for (let i = 0; i < 4; i++)
|
|
this._Individuals.push(bestP.Clone())
|
|
this._Individuals.push(this.bestP.Clone())
|
|
for (let i = 0; i < 3; i++)
|
|
this._Individuals.push(this._Individuals[1].Clone())
|
|
for (let i = 0; i < 2; i++)
|
|
this._Individuals.push(this._Individuals[2].Clone())
|
|
// 全部突变
|
|
for (const p of this._Individuals) {
|
|
if (p.Fitness !== undefined)
|
|
p.Mutate()
|
|
}
|
|
}
|
|
}
|
|
|
|
// 暂停
|
|
Suspend() {
|
|
console.timeEnd('1')
|
|
console.log(this.best, this.calcCount, NestCache.count1)
|
|
this._IsSuspend = true
|
|
console.log('暂停')
|
|
}
|
|
|
|
// 继续
|
|
Continue() {
|
|
this._IsSuspend = false
|
|
this.Run()
|
|
}
|
|
|
|
// 清理机台
|
|
Clear() {
|
|
}
|
|
}
|