|
|
|
@ -12,6 +12,14 @@ import { Point } from "./Point";
|
|
|
|
|
import { equaln } from "./Util";
|
|
|
|
|
import { Vector2 } from "./Vector2";
|
|
|
|
|
|
|
|
|
|
export interface PlaceState
|
|
|
|
|
{
|
|
|
|
|
p: Point;
|
|
|
|
|
area?: number;
|
|
|
|
|
hull?: Point[];
|
|
|
|
|
box?: Box2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class Container
|
|
|
|
|
{
|
|
|
|
|
ParentId: number = -1;//父亲id,-1表示默认的bin,-2开始表示余料,大于等于0表示板件
|
|
|
|
@ -33,24 +41,18 @@ export class Container
|
|
|
|
|
this.StatusKey = this.BinPath.Id.toString() + "," + this.PlaceType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get PlacedAll()
|
|
|
|
|
{
|
|
|
|
|
return this.Placed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get UseRatio()
|
|
|
|
|
{
|
|
|
|
|
let size = this.PlacedBox.getSize(new Vector2);
|
|
|
|
|
return size.x * size.y / this.BinPath.Area;
|
|
|
|
|
return this.PlacedBox.area / this.BinPath.Area;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
notPuts: Set<number>[] = [];
|
|
|
|
|
|
|
|
|
|
PutPart(part: Part): boolean
|
|
|
|
|
PrePut(part: Part): PlaceState
|
|
|
|
|
{
|
|
|
|
|
//------------无法容纳------------
|
|
|
|
|
//无法容纳
|
|
|
|
|
if (this.BinPath.Area - this.PlacedArea < part.State.Contour.Area)
|
|
|
|
|
return false;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
let cacheKey = this.StatusKey + "," + part.State.Contour.Id;
|
|
|
|
|
let cacheP = NestCache.PositionCache[cacheKey];
|
|
|
|
@ -58,23 +60,21 @@ export class Container
|
|
|
|
|
if (cacheP)
|
|
|
|
|
{
|
|
|
|
|
NestCache.count1++;
|
|
|
|
|
part.PlacePosition = cacheP;
|
|
|
|
|
this.AppendPartHoles(part);
|
|
|
|
|
return true;
|
|
|
|
|
return this.Calc(part, cacheP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let binNfp = this.BinPath.GetInsideNFP(part.State.Contour);
|
|
|
|
|
if (!binNfp || binNfp.length === 0) return false;
|
|
|
|
|
if (!binNfp || binNfp.length === 0) return;
|
|
|
|
|
|
|
|
|
|
//------------首个------------
|
|
|
|
|
//首个
|
|
|
|
|
if (this.Placed.length === 0)
|
|
|
|
|
{
|
|
|
|
|
part.PlacePosition = this.GetFarLeftP(binNfp);
|
|
|
|
|
this.AppendPartHoles(part);
|
|
|
|
|
NestCache.PositionCache[cacheKey] = part.PlacePosition;
|
|
|
|
|
return true;
|
|
|
|
|
let p = this.GetFarLeftP(binNfp);
|
|
|
|
|
NestCache.PositionCache[cacheKey] = p;
|
|
|
|
|
return this.Calc(part, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//快速退出
|
|
|
|
|
let noSet = NestCache.NoPutCache[this.StatusKey];
|
|
|
|
|
if (noSet) this.notPuts.push(noSet);
|
|
|
|
|
for (let set of this.notPuts)
|
|
|
|
@ -82,7 +82,7 @@ export class Container
|
|
|
|
|
if (set.has(part.State.Contour.Id))
|
|
|
|
|
{
|
|
|
|
|
NestCache.count2++;
|
|
|
|
|
return false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -95,10 +95,10 @@ export class Container
|
|
|
|
|
noSet = new Set([part.State.Contour.Id]);
|
|
|
|
|
NestCache.NoPutCache[this.StatusKey] = noSet;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//------------选择合适的放置点------------
|
|
|
|
|
//选择合适的放置点
|
|
|
|
|
let minArea: number = Infinity;
|
|
|
|
|
let translate: Point;
|
|
|
|
|
let bestBox: Box2;
|
|
|
|
@ -159,20 +159,88 @@ export class Container
|
|
|
|
|
|
|
|
|
|
if (translate)
|
|
|
|
|
{
|
|
|
|
|
part.PlacePosition = translate;
|
|
|
|
|
this.AppendPartHoles(part, false);
|
|
|
|
|
NestCache.PositionCache[cacheKey] = part.PlacePosition;
|
|
|
|
|
this.PlacedBox = bestBox ?? this.PlacedBox;
|
|
|
|
|
this.PlacedHull = bestHull;
|
|
|
|
|
return true;
|
|
|
|
|
NestCache.PositionCache[cacheKey] = translate;
|
|
|
|
|
if (!bestBox)
|
|
|
|
|
bestBox = this.PlacedBox.clone().union(part.State.Contour.BoundingBox.clone().translate({ x: translate.x * 1e-4, y: translate.y * 1e-4 }));
|
|
|
|
|
return { p: translate, area: minArea, box: bestBox, hull: bestHull };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Calc(part: Part, p: Point): PlaceState
|
|
|
|
|
{
|
|
|
|
|
let d: PlaceState = { p };
|
|
|
|
|
|
|
|
|
|
let m: Point = { x: p.x * 1e-4, y: p.y * 1e-4 };
|
|
|
|
|
|
|
|
|
|
if (this.PlacedBox)
|
|
|
|
|
d.box = this.PlacedBox.clone().union(part.State.Contour.BoundingBox.clone().translate(m));
|
|
|
|
|
else
|
|
|
|
|
d.box = part.State.Contour.BoundingBox.clone().translate(m);
|
|
|
|
|
|
|
|
|
|
//凸包
|
|
|
|
|
if (this.PlaceType === PlaceType.Hull)
|
|
|
|
|
{
|
|
|
|
|
if (!this.PlacedHull)
|
|
|
|
|
{
|
|
|
|
|
d.hull = TranslatePath(part.State.Contour.Points, m);
|
|
|
|
|
d.area = part.State.Contour.Area;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
d.hull = ConvexHull2D([...this.PlacedHull, ...TranslatePath(part.State.Contour.Points, m)]);
|
|
|
|
|
d.area = Area(d.hull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d.area = Math.abs(d.area);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
d.area = d.box.area;
|
|
|
|
|
}
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PutPart(p: Part, greedy = false): boolean
|
|
|
|
|
{
|
|
|
|
|
let bestD: PlaceState;
|
|
|
|
|
if (greedy)
|
|
|
|
|
{
|
|
|
|
|
let bestI: number;
|
|
|
|
|
for (let i = 0; i < p.RotatedStates.length; i++)
|
|
|
|
|
{
|
|
|
|
|
p.StateIndex = i;
|
|
|
|
|
let d = this.PrePut(p);
|
|
|
|
|
if (d && (!bestD || bestD.area > d.area ||
|
|
|
|
|
(
|
|
|
|
|
this.PlaceType === PlaceType.Hull
|
|
|
|
|
&& equaln(bestD.area, d.area, 0.1)
|
|
|
|
|
&& d.box.area < bestD.box.area
|
|
|
|
|
)
|
|
|
|
|
))
|
|
|
|
|
{
|
|
|
|
|
bestD = d;
|
|
|
|
|
bestI = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bestD)
|
|
|
|
|
p.StateIndex = bestI;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
bestD = this.PrePut(p);
|
|
|
|
|
|
|
|
|
|
if (bestD)
|
|
|
|
|
{
|
|
|
|
|
p.PlacePosition = bestD.p;
|
|
|
|
|
this.PlacedBox = bestD.box ?? this.PlacedBox;
|
|
|
|
|
this.PlacedHull = bestD.hull;
|
|
|
|
|
this.AppendPartHoles(p, false);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected GetNFPs(part: Part, binNfp: Point[][])
|
|
|
|
|
{
|
|
|
|
|
//------------计算nfp------------
|
|
|
|
|
//合并(零件和所有已经放置零件的NFP)
|
|
|
|
|
let nfps: SubjectInput[] = [];
|
|
|
|
|
for (let placedPart of this.Placed)
|
|
|
|
@ -207,7 +275,7 @@ export class Container
|
|
|
|
|
return finalNfp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected AppendPartHoles(part: Part, calc = true)
|
|
|
|
|
AppendPartHoles(part: Part, calc = true)
|
|
|
|
|
{
|
|
|
|
|
part.Container = this.BinPath.Id;
|
|
|
|
|
this.StatusKey += "," + part.State.Contour.Id;
|
|
|
|
@ -220,7 +288,7 @@ export class Container
|
|
|
|
|
if (this.PlaceType === PlaceType.Hull)
|
|
|
|
|
{
|
|
|
|
|
if (!this.PlacedHull)
|
|
|
|
|
this.PlacedHull = part.State.Contour.Points;
|
|
|
|
|
this.PlacedHull = TranslatePath(part.State.Contour.Points, m);
|
|
|
|
|
else
|
|
|
|
|
this.PlacedHull = ConvexHull2D([...this.PlacedHull, ...TranslatePath(part.State.Contour.Points, m)]);
|
|
|
|
|
}
|
|
|
|
@ -230,7 +298,7 @@ export class Container
|
|
|
|
|
if (this.PlacedBox)
|
|
|
|
|
this.PlacedBox.union(part.State.Contour.BoundingBox.clone().translate(m));
|
|
|
|
|
else
|
|
|
|
|
this.PlacedBox = part.State.Contour.BoundingBox.clone();
|
|
|
|
|
this.PlacedBox = part.State.Contour.BoundingBox.clone().translate(m);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -270,7 +338,6 @@ export class Container
|
|
|
|
|
part.PlacePosition = file.Read();
|
|
|
|
|
this.Placed.push(part);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this;
|
|
|
|
|
}
|
|
|
|
|
//对象将自身数据写入到文件.
|
|
|
|
|