使用缓存提高优化速度

pull/579/MERGE
ChenX 5 years ago
parent 212eeffa28
commit 85ee8e860d

@ -5,19 +5,22 @@ import { Point } from "./Point";
import { Clipper, PolyType, Paths, ClipType, PolyFillType } from "clipper-js-fpoint";
import { arrayRemoveIf } from "../Common/ArrayExt";
import { equaln, AsVector3 } from "../Geometry/GeUtils";
import { NestCache } from "./NestCache";
export class Container
{
Holes: Map<Part, Container[]> = new Map();
//放置状态
PlacedArea = 0;
PlacedBox: Box3;
PlacedBox: Box3 = new Box3();
Placed: Part[] = [];
OCS: Matrix4 = new Matrix4();
constructor(public Path?: Path)
StatusKey: string;
constructor(private Path: Path)
{
this.StatusKey = this.Path.Id.toString();
}
get PlacedAll()
@ -52,6 +55,17 @@ export class Container
}
}
let cacheKey = this.StatusKey + "," + part.State.PlaceOutline.Id;
//读取缓存位置
if (NestCache.PositionCache.has(cacheKey))
{
NestCache.count1++;
let p = NestCache.PositionCache.get(cacheKey);
part.PlacePosition = p;
this.AppendPartHoles(part);
return true;
}
//------------无法容纳------------
if (this.Path.Area - this.PlacedArea < part.State.PlaceOutline.Area)
return false;
@ -62,16 +76,16 @@ export class Container
//------------首个------------
if (this.Placed.length === 0)
{
let farLeftP = GetFarLeftP(binNfp);
part.PlacePosition = farLeftP;
this.Placed.push(part);
this.PlacedArea += part.State.PlaceOutline.Area;
this.PlacedBox = part.State.PlaceOutline.BoundingBox.clone().translate(AsVector3(farLeftP));
part.PlacePosition = GetFarLeftP(binNfp);
this.AppendPartHoles(part);
return true;
}
let combinedNfp: Paths = NestCache.NFPCache.get(cacheKey);
if (!combinedNfp)
{
combinedNfp = [];
NestCache.NFPCache.set(cacheKey, []);
//------------计算nfp------------
//合并(零件和所有已经放置零件的NFP)
let clipper = new Clipper();
@ -89,11 +103,16 @@ export class Container
}
}
let combinedNfp: Paths = [];
if (!clipper.Execute(ClipType.ctUnion, combinedNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero))
return false;
}
else
NestCache.count2++;
if (combinedNfp.length === 0) return false;
//binNfp 减去 combinedNfp 得到最终的nfp
clipper = new Clipper();
let clipper = new Clipper();
clipper.AddPaths(binNfp, PolyType.ptSubject, true);
clipper.AddPaths(combinedNfp, PolyType.ptClip, true);
let finalNfp: Paths = [];
@ -110,6 +129,9 @@ export class Container
if (finalNfp.length === 0)
return false;
//缓存combinedNfp
NestCache.NFPCache.set(cacheKey, combinedNfp);
/**
* (box).
* 使
@ -149,9 +171,6 @@ export class Container
if (translate)
{
part.PlacePosition = translate;
this.Placed.push(part);
this.PlacedBox.copy(minBox);
this.PlacedArea += part.State.PlaceOutline.Area;
this.AppendPartHoles(part);
return true;
}
@ -161,6 +180,14 @@ export class Container
private AppendPartHoles(part: Part)
{
this.Placed.push(part);
this.PlacedArea += part.State.PlaceOutline.Area;
this.PlacedBox.union(part.State.PlaceOutline.BoundingBox.clone().translate(AsVector3(part.PlacePosition)));
//位置缓存
this.StatusKey += "," + part.State.PlaceOutline.Id;
NestCache.PositionCache.set(this.StatusKey, part.PlacePosition);
part.Container = this;
let holes: Container[] = [];
for (let h of part.Holes)

@ -8,8 +8,10 @@ import { Part } from "../Part";
import { Path } from "../Path";
import { Point } from "../Point";
import { Circle2Points } from "./Curves2Parts";
import { NestCache } from "../NestCache";
export let DefaultBin = new Path([{ x: 0, y: 0 }, { x: 1220, y: 0 }, { x: 1220, y: 2440 }, { x: 0, y: 2440 }]);
DefaultBin.Id = -2;
let Rotations = [
[0, Math.PI],
@ -71,9 +73,15 @@ export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: numbe
export function ConverBoard2Part(board: Board): Part
{
let part = new Part();
let [, pts] = Curves2Points(board.ContourCurve as Polyline, true, 3);
let path: Path;
part.UserData = [board.ContourCurve.Clone()];
let path = new Path(pts);
if (!board.IsSpecialShape)
path = NestCache.CreatePath(board.Width, board.Height);
else
{
let [, pts] = Curves2Points(board.ContourCurve as Polyline, true, 3);
path = new Path(pts);
}
part.Init2(path, DefaultBin, Rotations[board.BoardProcessOption.lines]);
for (let m of board.BoardModeling)
{

@ -1,9 +1,8 @@
import { Part } from "./Part";
import { RandomIndex } from "./Random";
import { arrayLast } from "../Common/ArrayExt";
import { Container } from "./Container";
import { Part } from "./Part";
import { Path } from "./Path";
import { arrayLast } from "../Common/ArrayExt";
import { Sleep } from "../Common/Sleep";
import { RandomIndex } from "./Random";
/**
*
@ -20,7 +19,7 @@ export class Individual
{
//强壮程度 越低越好
Fitness: number;
constructor(public Parts?: Part[], public mutationRate = 0.8)
constructor(public Parts?: Part[], public mutationRate = 0.5)
{
}
@ -36,26 +35,34 @@ export class Individual
*/
Mutate()
{
if (this.mutationRate > 0.5)
for (let i = 0; i < this.Parts.length; i++)
{
let rand = Math.random();
if (rand < this.mutationRate * 0.2)
if (rand < this.mutationRate * 0.5)
{
//和下一个调换顺序
let j = i + 1;
if (j < this.Parts.length)
[this.Parts[i], this.Parts[j]] = [this.Parts[j], this.Parts[i]];
}
if (rand < this.mutationRate)
if (rand < this.mutationRate * 0.3)
{
this.Parts[i].Rotate();
break;
}
}
let index = RandomIndex(this.Parts.length);
this.Parts[index].Rotate();
//洗牌
let rand = Math.random();
if (rand < this.mutationRate)
{
let index = RandomIndex(this.Parts.length - 2);
let count = RandomIndex(this.Parts.length - 2 - index);
let count = Math.ceil(RandomIndex(this.Parts.length - 2 - index) * this.mutationRate * 2);
count = Math.ceil(count * this.mutationRate * 2);
if (count > 0)
{
let parts = this.Parts.splice(index, count);
@ -63,7 +70,7 @@ export class Individual
}
}
if (this.mutationRate > 0.1)
if (this.mutationRate > 0.2)
this.mutationRate -= 0.01;
return this;
}
@ -80,7 +87,7 @@ export class Individual
{
if (this.Containers.length > bestCount)
{
this.Fitness = this.Containers.length + 2;
this.Fitness = Math.ceil(bestCount) + 1;
return;
};
let container = new Container(bin);

@ -0,0 +1,54 @@
import { Path } from "./Path";
import { Point } from "./Point";
export class NestCache
{
static count1 = 0;
static count2 = 0;
static count3 = 0;
private static _pathId = -1;
static get PathId()
{
return this._pathId++;
}
/**
*
*/
static PositionCache = new Map<string, Point>();
static NFPCache = new Map<string, Point[][]>();
private static CacheRect = new Map<string, Path>();
/**
* 0
*/
static CreatePath(x: number, y: number): Path
{
let key = `${x.toFixed(2)},${y.toFixed(2)}`;
let path = this.CacheRect.get(key);
if (path) return path;
path = new Path([
{ x: 0, y: 0 },
{ x: x, y: 0 },
{ x: x, y: y },
{ x: 0, y: y },
]);
this.CacheRect.set(key, path);
return path;
}
static Clear()
{
this.count1 = 0;
this.count2 = 0;
this.count3 = 0;
this._pathId = -1;
this.CacheRect.clear();
this.PositionCache.clear();
this.NFPCache.clear();
}
}

@ -2,6 +2,7 @@ import { Path } from "./Path";
import { Part } from "./Part";
import { Individual } from "./Individual";
import { Sleep } from "../Common/Sleep";
import { NestCache } from "./NestCache";
/**
*
@ -41,7 +42,9 @@ export class OptimizeMachine
//启动
async Start()
{
console.log(this.Parts.length);
this._IsSuspend = false;
NestCache.Clear();
//1.初始化种群(n*个体)
this._Individuals = [
@ -65,12 +68,14 @@ export class OptimizeMachine
//2.执行
await this.Run();
}
calcCount = 0;
best = Infinity;
bestCount = 0;
private async Run()
{
console.time("1");
if (this.Parts.length === 0) return;
let best = Infinity;
let calcCount = 0;
//开始自然选择
while (!this._IsSuspend) //实验停止信号
{
@ -79,11 +84,12 @@ export class OptimizeMachine
let badV = -Infinity;
let badIndex: number;
// console.time();
//1.适应环境(放置零件)
for (let i = 0; i < this._Individuals.length; i++)
{
let p = this._Individuals[i];
await p.Evaluate(this.Bin, best);
await p.Evaluate(this.Bin, this.best);
if (p.Fitness < bestV)
{
bestV = p.Fitness;
@ -94,37 +100,46 @@ export class OptimizeMachine
badV = p.Fitness;
badIndex = i;
}
await Sleep(0);
}
calcCount += this._Individuals.length;
console.log(calcCount);
// console.timeEnd();
await Sleep(0);
this.calcCount += this._Individuals.length;
//自然选择
this._Individuals.push(bestP.Clone());//繁衍它
this._Individuals.splice(badIndex, 1);//杀死它
//除了最好的,其他人全部突变
for (let p of this._Individuals)
if (this.calcCount - this.bestCount > 8000)
{
if (p !== bestP && p.Fitness !== undefined)
p.Mutate();
for (let i of this._Individuals)
i.mutationRate = 0.5;
}
//回调最好的
if (bestP.Fitness < best)
if (bestP.Fitness < this.best)
{
console.log(bestP.Fitness);
best = bestP.Fitness;
this.best = bestP.Fitness;
console.timeLog("1", this.best, this.calcCount, NestCache.count1, NestCache.count2);
if (this.callBack)
{
await this.callBack(bestP);
}
}
//全部突变
for (let p of this._Individuals)
{
if (p.Fitness !== undefined)
p.Mutate();
}
}
}
//暂停
Suspend()
{
console.timeEnd("1");
console.log(this.best, this.calcCount, NestCache.count1, NestCache.count2);
this._IsSuspend = true;
console.log("暂停");
}

@ -30,11 +30,7 @@ import { AsVector3, equaln } from "../Geometry/GeUtils";
*/
export class Part
{
Container: Container;
//绑定数据
id: number;
//应该也是可序列化的实体
UserData: Curve[];
@ -96,6 +92,7 @@ export class Part
return this;
}
Init2(outline: Path, bin?: Path, rotations: number[] = [])
{
//初始化零件的状态集合
@ -123,24 +120,17 @@ export class Part
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;
return part;
}
//旋转起来,改变自身旋转状态(变异)
Rotate(): this
{
this.StateIndex = RandomIndex(this.RotatedStates.length);
return this;
}
ApplyPlace(containerCS: Matrix4)//容器的放置矩阵
{
let m = this.PlaceCS;
// m.multiplyMatrices(containerCS, m);
//ent.applyMatrix4(m);
}
}

@ -6,6 +6,7 @@ import { ISerialize } from "../DatabaseServices/ISerialize";
import { AsVector2, AsVector3, equaln } from "../Geometry/GeUtils";
import { OffsetPolyline } from "../GraphicsSystem/OffsetPolyline";
import { Point } from "./Point";
import { NestCache } from "./NestCache";
/**
*
@ -160,6 +161,18 @@ export class Path implements ISerialize
return nfps;
}
private _id: number;
get Id(): number
{
if (this._id === undefined)
this._id = NestCache.PathId;
return this._id;
}
set Id(v)
{
this._id = v;
}
protected _Polyline: Polyline;
get Polyline(): Polyline
{

@ -37,9 +37,8 @@ export class Command_TestPlace implements Command
map.set(pl, pl.OCS);
}
let container = new Container();
let container = new Container(binPath);
container.OCS = new Matrix4().setPosition(bp);
container.Path = binPath;
for (let i = 0; i < parts.length; i++)
{
let p = parts[i];

Loading…
Cancel
Save