同步优化代码

pull/629/MERGE
ChenX 5 years ago
parent 6c72917f71
commit 5c74b7f840

@ -18,6 +18,7 @@ const vendors = [
"dxf-parser",
"clipper-js-fpoint",
"pako",
"monotone-convex-hull-2d",
//如果你想调试threejs的代码,那么你应该注释掉下面的代码,然后重新构建dll
"three",
"three/examples/jsm/loaders/FBXLoader",

@ -81,6 +81,7 @@
"golden-layout": "^1.5.9",
"mobx": "^5.15.0",
"mobx-react": "^6.1.4",
"monotone-convex-hull-2d": "^1.0.1",
"p-queue": "^6.2.1",
"pako": "^1.0.10",
"react": "^16.12.0",

@ -1,34 +1,36 @@
import { Path } from "./Path";
import { Part } from "./Part";
import { Box3, Vector3, Matrix4 } from "three";
import { Point } from "./Point";
import { Clipper, PolyType, Paths, ClipType, PolyFillType } from "clipper-js-fpoint";
import { Clipper, ClipType, Paths, PolyFillType, PolyType } from "clipper-js-fpoint";
import { Box3, Matrix4, Vector3 } from "three";
import { arrayRemoveIf } from "../Common/ArrayExt";
import { equaln, AsVector3 } from "../Geometry/GeUtils";
import { AsVector3, equaln } from "../Geometry/GeUtils";
import { ConvexHull2D } from "./ConvexHull2D";
import { NestCache } from "./NestCache";
import { Part } from "./Part";
import { Path } from "./Path";
import { PlaceType } from "./PlaceType";
import { Point } from "./Point";
export class Container
{
Holes: Map<Part, Container[]> = new Map();
Holes: Container[] = [];
//放置状态
PlacedArea = 0;
PlacedBox: Box3 = new Box3();
Placed: Part[] = [];
PlacedHull: Point[];
PlaceType: PlaceType = PlaceType.Box;
OCS: Matrix4 = new Matrix4();
StatusKey: string;
constructor(protected BinPath: Path)
{
this.StatusKey = this.BinPath.Id.toString();
this.StatusKey = this.BinPath.Id.toString() + "," + this.PlaceType;
}
get PlacedAll()
{
let ps: Part[] = [...this.Placed];
for (let [, cs] of this.Holes)
for (let c of this.Holes)
{
for (let c of cs)
ps.push(...c.PlacedAll);
}
return ps;
@ -43,25 +45,19 @@ export class Container
PutPart(part: Part): boolean
{
//------------先放置在孔洞内------------
if (this.Holes)
{
for (let [, containers] of this.Holes)
{
for (let c of containers)
for (let c of this.Holes)
{
if (c.PutPart(part))
return true;
}
}
}
let cacheKey = this.StatusKey + "," + part.State.PlaceOutline.Id;
let cacheP = NestCache.PositionCache.get(cacheKey);
//读取缓存位置
if (NestCache.PositionCache.has(cacheKey))
if (cacheP)
{
NestCache.count1++;
let p = NestCache.PositionCache.get(cacheKey);
part.PlacePosition = p;
part.PlacePosition = cacheP;
this.AppendPartHoles(part);
return true;
}
@ -102,10 +98,13 @@ export class Container
}
}
let combinedNfp = [];
let combinedNfp: Point[][] = [];
if (!clipper.Execute(ClipType.ctUnion, combinedNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero))
return false;
combinedNfp = Clipper.CleanPolygons(combinedNfp, 0.1);
combinedNfp = combinedNfp.filter(nfp => { return nfp.length > 2; });
if (combinedNfp.length === 0) return false;
//binNfp 减去 combinedNfp 得到最终的nfp
@ -117,46 +116,55 @@ export class Container
return false;
//清理NFP
finalNfp = Clipper.CleanPolygons(finalNfp, 1);
arrayRemoveIf(finalNfp, nfp =>
{
return (nfp.length < 3 || Math.abs(Clipper.Area(nfp)) < 2);
});
finalNfp = Clipper.CleanPolygons(finalNfp, 0.1);
arrayRemoveIf(finalNfp, nfp => nfp.length < 2);
if (finalNfp.length === 0)
return false;
/**
* (box).
* 使
* TODO:使
*/
let minWidth = Infinity;
let minArea: number = Infinity;
let minX: number = Infinity;
let minY: number = Infinity;
let minBox: Box3;
let translate: Vector3;
let translate: Point;
let tempVec = new Vector3;
for (let nfp of finalNfp)
{
for (let p of nfp)
{
let translateTemp = AsVector3(p);
tempVec.set(p.x, p.y, 0);
let box2 = part.State.PlaceOutline.BoundingBox.clone();
box2.translate(translateTemp);
box2.translate(tempVec);
let rectBox = this.PlacedBox.clone().union(box2);
let size = rectBox.getSize(new Vector3());
//宽度的权重更大,使得零件的放置优先宽度较小
let area = (size.x * 1.1) * size.y;
if (area < minArea
|| (equaln(minArea, area) && translateTemp.x < minX)
|| (equaln(minArea, area) && translateTemp.y < minY))
switch (this.PlaceType)
{
case PlaceType.Box:
{
let area = size.x * size.y;
if (area < minArea ||
((equaln(area, minArea, 1))
&& (this.BinPath.Size.x > this.BinPath.Size.y ? p.x < translate.x : p.y < translate.y)))
{
translate = p;
minArea = area;
minWidth = size.x;
translate = translateTemp;
minX = translateTemp.x;
minY = translateTemp.y;
minBox = rectBox;
}
break;
}
case PlaceType.Hull:
{
let pts = Clipper.TranslatePath(part.State.PlaceOutline._Points, p);
let nhull = ConvexHull2D([...pts, ...this.PlacedHull]);
let area = Math.abs(Clipper.Area(nhull));
if (area < minArea ||
((equaln(area, minArea, 1))
&& (this.BinPath.Size.x > this.BinPath.Size.y ? p.x < translate.x : p.y < translate.y)))
{
translate = p;
minArea = area;
}
break;
}
default:
break;
}
}
}
@ -181,6 +189,15 @@ export class Container
this.StatusKey += "," + part.State.PlaceOutline.Id;
NestCache.PositionCache.set(this.StatusKey, part.PlacePosition);
//凸包
if (this.PlaceType === PlaceType.Hull)
{
if (!this.PlacedHull)
this.PlacedHull = part.State.PlaceOutline._Points;
else
this.PlacedHull = ConvexHull2D([...this.PlacedHull, ...part.State.PlaceOutline._Points]);
}
part.Container = this;
let holes: Container[] = [];
for (let h of part.Holes)
@ -193,7 +210,7 @@ export class Container
}
if (holes.length > 0)
this.Holes.set(part, holes);
this.Holes.push(...holes);
}
/**

@ -47,10 +47,13 @@ export function Polylin2Points(pl: Polyline, outside: boolean, knifRadius: numbe
let arc = pl.GetCurveAtIndex(i) as Arc;
let allAngle = arc.AllAngle;
let arcLength = arc.Length;
let splitCount = Math.round(allAngle / 0.4);
if (arc.Length < 300)
if (arcLength < 300)
splitCount = 2;
else
splitCount = Math.max(arcLength / 200, splitCount);
let radius = arc.Radius;
if (outside === bul > 0)

@ -0,0 +1,8 @@
import { Point } from "./Point";
import convexHull from 'monotone-convex-hull-2d';
export function ConvexHull2D(points: Point[]): Point[]
{
let pts = points.map(p => [p.x, p.y]);
let indexs = convexHull(pts);
return indexs.map(i => points[i]);
}

@ -0,0 +1,11 @@
/**
*
* 使使使
*/
export enum EvaluateType
{
Box = 1,
Width = 2,
Length = 3,
}

@ -3,6 +3,7 @@ import { Container } from "./Container";
import { Part } from "./Part";
import { Path } from "./Path";
import { RandomIndex } from "./Random";
import { PlaceType } from "./PlaceType";
/**
*
@ -35,7 +36,7 @@ export class Individual
*/
Mutate()
{
if (this.mutationRate > 0.5)
if (this.mutationRate > 0.6)
for (let i = 0; i < this.Parts.length; i++)
{
let rand = Math.random();
@ -46,9 +47,9 @@ export class Individual
if (j < this.Parts.length)
[this.Parts[i], this.Parts[j]] = [this.Parts[j], this.Parts[i]];
}
if (rand < this.mutationRate * 0.3)
if (rand < this.mutationRate)
{
this.Parts[i].Rotate();
this.Parts[i].Mutate();
break;
}
}
@ -58,19 +59,16 @@ export class Individual
if (rand < 0.5)
{
let index = RandomIndex(this.Parts.length - 2);
let count = Math.ceil(RandomIndex(this.Parts.length - 2 - index) * this.mutationRate * 2);
count = Math.ceil(count * this.mutationRate * 2) || 1;
let count = Math.ceil(RandomIndex(this.Parts.length - 2 - index) * this.mutationRate * 2) || 1;
let parts = this.Parts.splice(index, count);
this.Parts.push(...parts);
}
else
{
let index = RandomIndex(this.Parts.length);
this.Parts[index].Rotate();
}
this.Parts[index].Mutate();
if (this.mutationRate > 0.2)
this.mutationRate -= 0.01;
this.mutationRate -= 0.004;
return this;
}
@ -78,7 +76,7 @@ export class Individual
/**
*
*/
async Evaluate(bin: Path, bestCount: number)
Evaluate(bin: Path, bestCount: number)
{
let parts = this.Parts;
this.Containers = [];
@ -90,7 +88,8 @@ export class Individual
return;
};
let container = new Container(bin);
if (this.mutationRate > 0.21)
container.PlaceType = PlaceType.Box;
if (this.mutationRate > 0.5)
{
let area = 0;
let maxP: Part;

@ -1,11 +1,12 @@
import { Path } from "./Path";
import { Part } from "./Part";
import { Individual } from "./Individual";
import { Sleep } from "../Common/Sleep";
import { NestCache } from "./NestCache";
import { Vector3 } from "three";
import { arrayRemoveIf } from "../Common/ArrayExt";
import { Sleep } from "../Common/Sleep";
import { MoveMatrix } from "../Geometry/GeUtils";
import { Vector3 } from "three";
import { Individual } from "./Individual";
import { NestCache } from "./NestCache";
import { Part } from "./Part";
import { Path } from "./Path";
import { ShuffleArray } from "./Shuffle";
/**
*
@ -62,27 +63,13 @@ export class OptimizeMachine
console.log(this.Parts.length);
this._IsSuspend = false;
NestCache.Clear();
//1.初始化种群(n*个体)
this._Individuals = [
new Individual(this.Parts, 0.8),
new Individual(this.Parts.map(p => p.Clone()).reverse(), 0.8),
];
//对插洗牌
let parts: Part[] = [];
let count = Math.floor(this.Parts.length / 2);
for (let i = 0; i < count; i++)
parts.push(this.Parts[i].Clone(), this.Parts[this.Parts.length - 1 - i].Clone());
if ((this.Parts.length & 1) === 1)
parts.push(this.Parts[count].Clone());
this._Individuals.push(new Individual(parts, 0.8));
for (let i = 3; i < this.Config.PopulationCount; i++)
this.Parts.sort((p1, p2) => p2.State.PlaceOutline.Area - p1.State.PlaceOutline.Area);
this._Individuals = [new Individual(this.Parts, 0.8)];
for (let i = 1; i < this.Config.PopulationCount; i++)
{
let p = this._Individuals[i % 3].Mutate();
p.mutationRate = 0.8;
this._Individuals.push(p);
let parts = this.Parts.map(p => p.Clone());
ShuffleArray(parts);
this._Individuals.push(new Individual(parts, 0.8));
}
//2.执行
await this.Run();
@ -90,6 +77,7 @@ export class OptimizeMachine
calcCount = 0;
best = Infinity;
bestP: Individual;
bestCount = 0;
private async Run()
{
@ -98,47 +86,30 @@ export class OptimizeMachine
//开始自然选择
while (!this._IsSuspend) //实验停止信号
{
let bestV = Infinity;
let bestP: Individual;
let badV = -Infinity;
let badIndex: number;
this.calcCount += this._Individuals.length;
let goBack = this.calcCount - this.bestCount > 8000;
// console.time();
//1.适应环境(放置零件)
for (let i = 0; i < this._Individuals.length; i++)
{
let p = this._Individuals[i];
await p.Evaluate(this.Bin, this.best);
if (p.Fitness < bestV)
{
bestV = p.Fitness;
bestP = p;
}
if (p.Fitness > badV)
{
badV = p.Fitness;
badIndex = i;
}
p.Evaluate(this.Bin, this.best);
if (goBack)
p.mutationRate = 0.5;
}
// console.timeEnd();
await Sleep(0);
this.calcCount += this._Individuals.length;
//自然选择
this._Individuals.push(bestP.Clone());//繁衍它
this._Individuals.splice(badIndex, 1);//杀死它
if (this.calcCount - this.bestCount > 8000)
{
for (let i of this._Individuals)
i.mutationRate = 0.5;
}
if (this.calcCount % 5000 === 0)
console.timeLog("1", this.best, this.calcCount, NestCache.count1, NestCache.count2);
this._Individuals.sort((i1, i2) => i1.Fitness - i2.Fitness);
let 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)
{
@ -146,6 +117,14 @@ export class OptimizeMachine
}
}
//自然选择
this._Individuals.splice(-10);//杀死它
for (let i = 0; i < 5; i++)
this._Individuals.push(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 (let p of this._Individuals)
{

@ -1,10 +1,10 @@
import { Matrix4 } from "three";
import { FixIndex } from "../Common/Utils";
import { Curve } from "../DatabaseServices/Entity/Curve";
import { AsVector3, equaln } from "../Geometry/GeUtils";
import { Container } from "./Container";
import { PartState } from "./PartState";
import { Path } from "./Path";
import { PlaceType } from "./PlaceType";
import { Point } from "./Point";
import { RandomIndex } from "./Random";
@ -129,12 +129,9 @@ export class Part
}
//旋转起来,改变自身旋转状态(变异)
Rotate(): this
Mutate(): this
{
let newIndex = RandomIndex(this.RotatedStates.length);
if (newIndex === this.StateIndex)
newIndex = FixIndex(this.StateIndex + 1, this.RotatedStates);
this.StateIndex = newIndex;
this.StateIndex = RandomIndex(this.RotatedStates.length, this.StateIndex);
return this;
}
}

@ -15,7 +15,7 @@ import { Point } from "./Point";
*/
export class Path implements ISerialize
{
private _Points: Point[];
_Points: Point[];
private OutsideNFPCache: Map<Path, Point[][]> = new Map();
private InsideNFPCache: Map<Path, Point[][]> = new Map();
@ -99,13 +99,19 @@ export class Path implements ISerialize
for (let np of nfp)
{
let { x, y } = np;
let isOut = false;
for (let p of path._Points)
{
tp.set(p.x + x, p.y + y, 0);
let dir = this.Check.GetPointAtCurveDir(tp);
if (dir === 0) continue;
if (dir === -1) return false;
else break;
else
{
if (path.Area < this.Area) return true;
isOut = true;
break;
}
}
for (let p of this._Points)
@ -114,7 +120,11 @@ export class Path implements ISerialize
let dir = path.Check.GetPointAtCurveDir(tp);
if (dir === 0) continue;
if (dir === -1) return false;
else break;
else
{
if (isOut || this.Area < path.Area) return true;
break;
}
}
}
}
@ -155,6 +165,14 @@ export class Path implements ISerialize
else
nfps = this.GetNFPs(path, true);
this.OutsideNFPCache.set(path, nfps);
//虽然有这种神奇的特性,但是好像并不会提高性能。
// path.OutsideNFPCache[this.id] = (this, nfps.map(nfp =>
// {
// return nfp.map(p =>
// {
// return { x: -p.x, y: -p.y };
// });
// }));
return nfps;
}
GetInsideNFP(path: Path): Point[][]

@ -1,10 +1,6 @@
export enum PlaceType
{
MinBox = 0,
MinX = 1,
MinY = 2,
MaxX = 3,
MaxY = 4,
Box = 0,
Hull = 1,
}

@ -1,8 +1,10 @@
import { FixIndex } from "../Common/Utils";
export function RandomIndex(count: number): number
export function RandomIndex(count: number, exclude?: number): number
{
let r = Math.random();
let index = Math.floor(r * count);
if (index === count) return 0;
if (index === count) index = 0;
if (index === exclude) index = FixIndex(index + 1, count);
return index;
}

@ -0,0 +1,8 @@
export function ShuffleArray<T = any>(array: T[])
{
for (let i = array.length - 1; i > 0; i--)
{
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}

@ -16,15 +16,12 @@ export class TestContainer extends Container
//------------先放置在孔洞内------------
if (this.Holes)
{
for (let [, containers] of this.Holes)
{
for (let c of containers)
for (let c of this.Holes)
{
if (c.PutPart(part))
return true;
}
}
}
let cacheKey = this.StatusKey + "," + part.State.PlaceOutline.Id;
//读取缓存位置

@ -0,0 +1,24 @@
import { app } from "../../ApplicationServices/Application";
import { Point } from "../../DatabaseServices/Entity/Point";
import { PromptStatus } from "../../Editor/PromptResult";
import { ConvexHull2D } from "../ConvexHull2D";
import { Path2Polyline } from "../Converter/Path2Polyline";
import { HotCMD } from "../../Hot/HotCommand";
@HotCMD
export class Command_TestHull
{
async exec()
{
let ssres = await app.Editor.GetSelection({ Filter: { filterTypes: [Point] } });
if (ssres.Status !== PromptStatus.OK) return;
let ptents = ssres.SelectSet.SelectEntityList as Point[];
let pts = ptents.map(p => p.Position);
let hull = ConvexHull2D(pts);
let pl = Path2Polyline(hull);
pl.CloseMark = true;
app.Database.ModelSpace.Append(pl);
}
}
Loading…
Cancel
Save