!1407 功能:前置余料和余料自动分析

pull/1407/MERGE
ChenX 4 years ago
parent 5cd43ff1c0
commit b7e635625a

@ -3,7 +3,7 @@
"typescript.tsdk": "node_modules\\typescript\\lib",
//
"editor.tabSize": 4,
"editor.formatOnPaste": true,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.insertSpaces": true,
"editor.detectIndentation": false,

@ -1,33 +1,45 @@
import { Vector3 } from "three";
import { ClipType, PolyFillType } from "js-angusj-clipper/web";
import { Vector2 } from "three";
import { app } from "../../ApplicationServices/Application";
import { EntityRef } from "../../DatabaseServices/Entity/EntityRef";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { Command } from "../../Editor/CommandMachine";
import { PromptStatus } from "../../Editor/PromptResult";
import { HotCMD } from "../../Hot/HotCommand";
import { clipperCpp, InitClipperCpp } from "../../Nest/Common/ClipperCpp";
import { Path2Polyline } from "../../Nest/Converter/Path2Polyline";
import { PathScale } from "../../Nest/Core/Path";
import { TestDraw } from "../test/TestUtil";
@HotCMD
export class Test implements Command
{
async exec()
{
for (let i = 1; i < 6; i++)
{
let e = new EntityRef("/Data/ASSETS/DXAA_000" + i);
e.Position = new Vector3((i - 1) * 5000);
app.Database.ModelSpace.Append(e);
}
let promise = InitClipperCpp();
for (let i = 1; i < 98; i++)
{
let e = new EntityRef("/Data/ASSETS/DZAA_000" + i);
e.Position = new Vector3((i - 1) * 5000, 3000);
app.Database.ModelSpace.Append(e);
}
let plss = await app.Editor.GetSelection({ Filter: { filterTypes: [Polyline] } });
if (plss.Status !== PromptStatus.OK) return;
let pls = plss.SelectSet.SelectEntityList as Polyline[];
let bin = pls.shift();
await promise;
let finalNfp = clipperCpp.lib.clipToPaths({
subjectInputs: [{ data: PathScale(bin.GetStretchPoints(), 1e4), closed: true }],
clipInputs: pls.map(pl => { return { data: PathScale(pl.GetStretchPoints(), 1e4) }; }),
clipType: ClipType.Difference,
subjectFillType: PolyFillType.NonZero
});
finalNfp = clipperCpp.lib.simplifyPolygons(finalNfp);
for (let i = 1; i < 67; i++)
for (let nfp of finalNfp)
{
let e = new EntityRef("/Data/ASSETS/DYAA_000" + i);
e.Position = new Vector3((i - 1) * 5000, 5000);
app.Database.ModelSpace.Append(e);
let pl = Path2Polyline(nfp.map(p => new Vector2(p.x * 1e-4, p.y * 1e-4)));
TestDraw(pl);
}
}
}

@ -206,6 +206,8 @@ import { Command_TestNFP } from "../Nest/Test/TestNFP";
import { Command_TestPlace } from "../Nest/Test/TestPlace";
import { Command_TestSaveYHData } from "../Nest/Test/TestSaveYHData";
import { Command_TestSimply } from "../Nest/Test/TestSimply";
import { Command_TestSimplyOddments } from "../Nest/Test/TestSimplyOddments";
import { Command_TestSum } from "../Nest/Test/TestSum";
import { Command_TestYH2 } from "../Nest/Test/TestYH2";
import { Command_TestYHSingle } from "../Nest/Test/TestYHSingle";
import { Command_TestYHWorker } from "../Nest/Test/TestYHWorker";
@ -504,6 +506,8 @@ export function registerCommand()
commandMachine.RegisterCommand("testYHSingle", new Command_TestYHSingle());
commandMachine.RegisterCommand("testYHDraw", new Command_TestDrawYHData());
commandMachine.RegisterCommand("testYHSave", new Command_TestSaveYHData());
commandMachine.RegisterCommand("testSum", new Command_TestSum());
commandMachine.RegisterCommand("TestSimplyOddments", new Command_TestSimplyOddments());
commandMachine.RegisterCommand("editorlattice", new EditorLattice());

@ -32,7 +32,7 @@ interface PartPlacedContainerState
*/
export class Container
{
ParentId: number = -1;//父亲id,-1表示默认的bin,-2开始表示余料,大于等于0表示板件
ParentId: number = -1;//容器bin来源 -1表示默认的bin,大于等于0表示来自板件网洞
ChildrenIndex: number = 0;//网洞的索引位置
ParentM: Point;//在旋转网洞时,和非旋转网洞时表现不一样. (网洞相对于父零件的位置,如果是使用旋转网洞则表示网洞的最下角位置)

@ -22,11 +22,12 @@ let ComparePointKeys = ["xy", "yx"];
*/
export class Individual
{
constructor(public Parts?: Part[], public mutationRate = 0.5, private bin?: Path, private remBins: Path[] = []) { }
constructor(public Parts?: Part[], public mutationRate = 0.5, private bin?: Path, private OddmentsBins: Path[] = []) { }
Fitness: number; //(评估健康) 大板个数-1 + 最后一块板的利用率
Containers: Container[]; //大板列表(已排
HoleContainers: Container[];//网洞列表
OddmentsContainers: Container[];//余料列表
/**
*
*/
@ -37,10 +38,15 @@ export class Individual
this.Containers = [];
this.HoleContainers = [];
//初始化余料列表
this.OddmentsContainers = this.OddmentsBins.map(path => new Container(path, type ?? RandomIndex(3), ComparePointKeys[RandomIndex(2)]));
this.InitHoleContainers();
//余料优先
let parts = this.Parts.filter(p => !this.OddmentsContainers.some(odd => odd.PutPart(p)));
//网洞优先
let parts = this.Parts.filter(p => !this.HoleContainers.some(hole => hole.PutPart(p)));
parts = parts.filter(p => !this.HoleContainers.some(hole => hole.PutPart(p)));
while (parts.length > 0)
{
@ -83,7 +89,7 @@ export class Individual
parts = parts.filter(p => !container.PutPart(p, true));
this.Containers.push(container);
}
this.Fitness = this.Containers.length - 1 + arrayLast(this.Containers).UseRatio;
this.Fitness = this.Containers.length - 1 + arrayLast(this.Containers)?.UseRatio ?? 0;
}
/**
@ -95,6 +101,9 @@ export class Individual
*/
private EvaluateOfUseRotateHole(bestCount: number, greedy = false, type?: PlaceType)
{
//初始化余料列表
this.OddmentsContainers = this.OddmentsBins.map(path => new Container(path, type ?? RandomIndex(3), ComparePointKeys[RandomIndex(2)]));
this.Containers = [];
this.HoleContainers = [];
let parts = this.Parts.concat();
@ -111,13 +120,18 @@ export class Individual
const PutPart = (part: Part, greedy: boolean): boolean =>
{
//先放置在网洞内
let isOk = holes.some(h =>
let isOk =
this.OddmentsContainers.some(oc => oc.PutPart(part, greedy)) || //余料
holes.some(h =>
{
let isOk = h.PutPart(part, greedy);
return isOk;
}) || container.PutPart(part, greedy);
}) || //网洞
container.PutPart(part, greedy); //大板
if (isOk)
this.AppendHoles(part, holes);
return isOk;
};
@ -149,12 +163,15 @@ export class Individual
else if (PutPart(maxP, greedy))
arrayRemoveOnce(parts, maxP);
}
parts = parts.filter(p => !PutPart(p, greedy));
if (!greedy)//如果没有贪心,排完后在贪心一下
parts = parts.filter(p => !PutPart(p, true));
if (container.PlacedParts.length)
this.Containers.push(container);
}
this.Fitness = this.Containers.length - 1 + arrayLast(this.Containers).UseRatio;
this.Fitness = this.Containers.length - 1 + arrayLast(this.Containers)?.UseRatio ?? 0;
}
private AppendHoles(part: Part<any, any>, holes: Container[])
@ -190,7 +207,7 @@ export class Individual
Clone()
{
let p = new Individual(this.Parts.map(p => p.Clone()), this.mutationRate, this.bin, this.remBins);
let p = new Individual(this.Parts.map(p => p.Clone()), this.mutationRate, this.bin, this.OddmentsBins);
p.Mutate();
return p;
}
@ -248,6 +265,8 @@ export class Individual
//对象从文件中读取数据,初始化自身
ReadFile(file: NestFiler)
{
let ver = file.Read();//ver
this.Fitness = file.Read();
let count = file.Read() as number;
this.Containers = [];
@ -258,11 +277,17 @@ export class Individual
this.HoleContainers = [];
for (let i = 0; i < count; i++)
this.HoleContainers.push(new Container().ReadFile(file, this.Parts));
count = file.Read();
this.OddmentsContainers = [];
for (let i = 0; i < count; i++)
this.OddmentsContainers.push(new Container().ReadFile(file, this.Parts));
}
//对象将自身数据写入到文件.
WriteFile(f: NestFiler)
{
f.Write(1);//ver
f.Write(this.Fitness);
f.Write(this.Containers.length);
for (let c of this.Containers)
@ -271,6 +296,10 @@ export class Individual
f.Write(this.HoleContainers.length);
for (let c of this.HoleContainers)
c.WriteFile(f);
f.Write(this.OddmentsContainers.length);
for (let c of this.OddmentsContainers)
c.WriteFile(f);
}
//#endregion
}

@ -9,8 +9,9 @@ import { PathGeneratorSingle } from "./PathGenerator";
*/
export class NestDatabase
{
Paths: Path[]; //所有的Path都在这里
Bin: Path; //默认的容器
OddmentsBins: Path[];//余料容器列表
Paths: Path[]; //所有的Path都在这里
Parts: Part[]; //所有的零件
//#region -------------------------File-------------------------
@ -36,6 +37,16 @@ export class NestDatabase
part.ReadFile(file);
this.Parts.push(part);
}
count = file.Read();
this.OddmentsBins = [];
for (let i = 0; i < count; i++)
{
let path = new Path();
path.ReadFile(file);
this.OddmentsBins.push(path);
}
return this;
}
//对象将自身数据写入到文件.
@ -50,6 +61,13 @@ export class NestDatabase
file.Write(this.Parts.length);
for (let part of this.Parts)
part.WriteFile(file);
if (!this.OddmentsBins) this.OddmentsBins = [];
file.Write(this.OddmentsBins.length);
for (let path of this.OddmentsBins)
path.WriteFile(file);
return this;
}
//#endregion

@ -20,15 +20,17 @@ export class OptimizeMachine
Config: {
PopulationCount: number;//种群个数
};
//容器板
Bin: Path;
//机台上的零件
Parts: Part[];
//计算重复的零件 TODO:需要对相同的零件提取出来
PartCount: number[] = [];
Bin: Path; //默认的容器
OddmentsBins: Path[];//余料容器列表
Parts: Part[]; //所有的零件
// //计算重复的零件 TODO:需要对相同的零件提取出来
// PartCount: number[] = [];
private _IsSuspend = false;
protected _Individuals: Individual[];
protected _Individuals: Individual[];//个体列表
constructor()
{
@ -41,12 +43,13 @@ export class OptimizeMachine
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);
}
// //计算重复的零件(暂时不用)
// 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>;
@ -59,7 +62,7 @@ export class OptimizeMachine
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._Individuals = [new Individual(this.Parts, 0.8, this.Bin, this.OddmentsBins)];
for (let i = 1; i < this.Config.PopulationCount; i++)
{
let parts = this.Parts.map(p => p.Clone());
@ -72,7 +75,7 @@ export class OptimizeMachine
parts[i].Mutate();
}
}
this._Individuals.push(new Individual(parts, 0.8, this.Bin));
this._Individuals.push(new Individual(parts, 0.8, this.Bin, this.OddmentsBins));
}
//2.执行
await this.Run();

@ -13,6 +13,7 @@ ctx.addEventListener("message", async (event) =>
let m = new OptimizeMachine;
m.Bin = db.Bin;
m.OddmentsBins = db.OddmentsBins;
m.PutParts(db.Parts);
m.callBack = async (inv) =>

@ -0,0 +1,80 @@
import { ClipInput, ClipType, EndType, JoinType, PolyFillType } from "js-angusj-clipper/web";
import { clipperCpp } from "../Common/ClipperCpp";
import { Container } from "./Container";
import { NestCache } from "./NestCache";
import { Path, PathScale, TranslatePath } from "./Path";
const SquarePath = NestCache.CreatePath(60, 60, 0);
const CanPutPaths = [
NestCache.CreatePath(200, 200, 0),
NestCache.CreatePath(600, 100, 0),
NestCache.CreatePath(100, 600, 0)
];
/**
* ()
* @param container
* @param binPath bin
* @param [knifeRadius=3.5] (便)
* @returns Path[] OrigionMinPoint
*/
export function ParseOddments(container: Container, binPath: Path, knifeRadius: number = 3.5, squarePath: Path = SquarePath, canPutPaths: Path[] = CanPutPaths): Path[]
{
//构建轮廓数据
let partPaths: ClipInput[] = container.PlacedParts.map(part =>
{
//直接在这里偏移,而不缓存,应该没有性能问题
let newPts = clipperCpp.lib.offsetToPaths({
delta: knifeRadius * 1e4,
offsetInputs: [{ data: part.State.Contour.BigIntPoints, joinType: JoinType.Miter, endType: EndType.ClosedPolygon }]
})[0];
let path = TranslatePath(newPts, { x: part.PlacePosition.x - 5e3, y: part.PlacePosition.y - 5e3 });//因为移动了0.5,0.5,所以这里也要移动0.5
return { data: path };
});
//所有的余料(使用布尔差集)
let oddmentsPolygon = clipperCpp.lib.clipToPaths({
subjectInputs: [{ data: binPath.BigIntPoints, closed: true }],
clipInputs: partPaths,
clipType: ClipType.Difference,
subjectFillType: PolyFillType.NonZero
});
//简化结果,避免自交
oddmentsPolygon = clipperCpp.lib.simplifyPolygons(oddmentsPolygon);
let OddmentsPaths: Path[] = [];
for (let polygon of oddmentsPolygon)
{
let polygonPath = new Path(PathScale(polygon, 1e-4));
//先获取内部的nfp
let insideNFPS = polygonPath.GetInsideNFP(squarePath);
if (!insideNFPS) continue;
for (let nfp of insideNFPS)
{
let nfpPath = new Path(PathScale(nfp, 1e-4));
//通过内部nfp还原实际轮廓
let sumPolygons = clipperCpp.lib.minkowskiSumPath(nfpPath.BigIntPoints, SquarePath.BigIntPoints, true);
sumPolygons = clipperCpp.lib.simplifyPolygons(sumPolygons);
for (let poly of sumPolygons)
{
if (clipperCpp.lib.area(poly) < 0) continue;//移除内部的,无意义的
let tempPath = new Path(PathScale(poly, 1e-4));
if (canPutPaths.some(p => tempPath.GetInsideNFP(p)?.length))//能塞的下制定的轮廓才会被留下
{
//设置轮廓的位置
tempPath.OrigionMinPoint.x += nfpPath.OrigionMinPoint.x + polygonPath.OrigionMinPoint.x;
tempPath.OrigionMinPoint.y += nfpPath.OrigionMinPoint.x + polygonPath.OrigionMinPoint.x;
OddmentsPaths.push(tempPath);
}
}
}
}
return OddmentsPaths;
}

@ -78,14 +78,11 @@ export class Path
let nfps = clipperCpp.lib.minkowskiSumPath(this.BigIntPoints, path.MirrorPoints, true);
//必须删除自交,否则将会出错
let nfps_remove_self_intersections: Paths = [];
for (let nfp of nfps)
nfps_remove_self_intersections.push(...clipperCpp.lib.simplifyPolygon(nfp));
nfps = nfps_remove_self_intersections.filter((nfp) =>
nfps = clipperCpp.lib.simplifyPolygons(nfps);
nfps = nfps.filter((nfp) =>
{
let area = Area(nfp);
if (area > 1) return outside;//第一个不一定是外轮廓,但是面积为正时肯定为外轮廓
// if (area > 1) return outside;//第一个不一定是外轮廓,但是面积为正时肯定为外轮廓 (因为使用了简化多段线,所以这个代码已经不能有了)
if (Math.abs(area) < 10) return false;//应该不用在移除这个了
let { x, y } = nfp[0];

@ -1,4 +1,4 @@
import { Matrix4, Vector3 } from "three";
import { Matrix4, Vec2, Vector3 } from "three";
import { TestDraw } from "../../Add-on/test/TestUtil";
import { app } from "../../ApplicationServices/Application";
import { UpdateDraw } from "../../Common/Status";
@ -8,17 +8,17 @@ import { Curve } from "../../DatabaseServices/Entity/Curve";
import { Entity } from "../../DatabaseServices/Entity/Entity";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { PromptStatus } from "../../Editor/PromptResult";
import { IdentityMtx4, YAxis, ZAxis } from "../../Geometry/GeUtils";
import { Vec3 } from "../../Geometry/IVec3";
import { AsVector3, IdentityMtx4, YAxis, ZAxis } from "../../Geometry/GeUtils";
import { InitClipperCpp } from "../Common/ClipperCpp";
import { ConverBoard2Part } from "../Converter/ConverBoard2Part";
import { Curves2Parts } from "../Converter/Curves2Parts";
import { Curves2Parts, CurveWrap } from "../Converter/Curves2Parts";
import { Path2Polyline } from "../Converter/Path2Polyline";
import { DefaultBin } from "../Core/DefaultBin";
import { GNestConfig } from "../Core/GNestConfig";
import { Individual } from "../Core/Individual";
import { NestCache } from "../Core/NestCache";
import { NestDatabase } from "../Core/NestDatabase";
import { ParseOddments } from "../Core/ParseOddments";
import { Part, PartGroup } from "../Core/Part";
import { Path } from "../Core/Path";
import { PathGeneratorSingle } from "../Core/PathGenerator";
@ -30,7 +30,7 @@ let v = new Vector3;
let mirrorMtx = new Matrix4().makeBasis(new Vector3(-1), YAxis, ZAxis);
let mirrorMoveMtx = new Matrix4();
export function PlaceCS(part: Part, move?: Vec3): Matrix4
export function PlaceCS(part: Part, move?: Vec2): Matrix4
{
r.makeRotationZ(part.State.Rotation);
m.setPosition(-part.State.OrigionMinPoint.x, -part.State.OrigionMinPoint.y, 0);
@ -63,6 +63,8 @@ function ApplyMatrix(en: Entity, mtx: Matrix4)
}
let binCurves: Polyline[] = [];
const OddmentsCurves: Polyline[] = [];
export function DrawBin(count: number, binPath: Path)
{
for (let i = 0; i < binCurves.length; i++)
@ -80,8 +82,15 @@ export function DrawBin(count: number, binPath: Path)
app.Editor.UpdateScreen();
};
const MOVEVEC = { x: -0.5, y: -0.5 };
export function Place(inv: Individual, parts: Part[], binPath: Path)
{
//清空余料绘制
for (let pl of OddmentsCurves)
pl.Erase();
OddmentsCurves.length = 0;
for (let part of parts)
{
part.Parent = undefined;
@ -92,13 +101,27 @@ export function Place(inv: Individual, parts: Part[], binPath: Path)
}
DrawBin(inv.Containers.length, binPath);
let moveVec = new Vector3(-0.5, -0.5, 0);
//放置第一层的板
for (let i = 0; i < inv.Containers.length; i++)
for (let i = -inv.OddmentsContainers.length; i < inv.Containers.length; i++)
{
let j = 1;
for (let part of inv.Containers[i].PlacedParts)
let container = i < 0 ? inv.OddmentsContainers[i + inv.OddmentsContainers.length] : inv.Containers[i];
//暂时不分析余料的余料,网洞的余料
if (i >= 0)
{
let odd = ParseOddments(container, binPath);
for (let p of odd)
{
let pl = Path2Polyline(p.Points);
pl.Position = AsVector3(p.OrigionMinPoint).add(new Vector3(1.1 * i * 1220, 2450));
OddmentsCurves.push(pl);
TestDraw(pl);
}
}
for (let part of container.PlacedParts)
{
//组合基因
if (part instanceof PartGroup)
@ -107,7 +130,7 @@ export function Place(inv: Individual, parts: Part[], binPath: Path)
{
let partOld = parts[p.Id];
partOld.PlaceIndex = i;
partOld.PlaceCS = PlaceCS(p, moveVec);
partOld.PlaceCS = PlaceCS(p, MOVEVEC);
if (i !== 0)
partOld.PlaceCS = new Matrix4().setPosition(new Vector3(1.1 * i * 1220)).multiply(partOld.PlaceCS);
@ -122,7 +145,7 @@ export function Place(inv: Individual, parts: Part[], binPath: Path)
{
let partOld = parts[part.Id];
partOld.PlaceIndex = i;
partOld.PlaceCS = PlaceCS(part, moveVec);
partOld.PlaceCS = PlaceCS(part, MOVEVEC);
if (i !== 0)
partOld.PlaceCS = new Matrix4().setPosition(new Vector3(1.1 * i * 1220)).multiply(partOld.PlaceCS);
@ -209,10 +232,9 @@ export function Place(inv: Individual, parts: Part[], binPath: Path)
//计算网洞的位置信息
for (let hole of inv.HoleContainers)
{
let p = new Vector3(hole.ParentM.x, hole.ParentM.y, 0);
for (let part of hole.PlacedParts)
{
part.PlaceCS = PlaceCS(part, p);
part.PlaceCS = PlaceCS(part, hole.ParentM);
part.Parent = parts[hole.ParentId];
}
}
@ -243,6 +265,40 @@ export function Place(inv: Individual, parts: Part[], binPath: Path)
export async function InitParts(binPath = DefaultBin): Promise<NestDatabase>
{
//清理缓存
PathGeneratorSingle.Clear();
NestCache.Clear();
DefaultBin.InsideNFPCache = [];
binPath.Id = undefined;
PathGeneratorSingle.RegisterId(binPath);
//选择余料
let oddmentsPathRes = await app.Editor.GetSelection({ AllowNone: true, Msg: "请选择余料轮廓", Filter: { filterTypes: [Polyline, Circle] } });
if (oddmentsPathRes.Status === PromptStatus.Cancel) return;
let odds = oddmentsPathRes.SelectSet.SelectEntityList as (Circle | Polyline)[];
let oddpaths = odds.map((curve, index) =>
{
let warp = new CurveWrap(curve, 3.5, false);
let pts = warp.GetInsidePoints();
//把余料放置好位置
let curveC = curve.Clone();
curveC.ApplyMatrix(new Matrix4().setPosition(new Vector3((index - odds.length) * 1.1 * 1220).sub(curveC.BoundingBox.min)));
TestDraw(curveC);
let pl = new Polyline().Rectangle(1220, 2440);
pl.Position = new Vector3((index - odds.length) * 1.1 * 1220);
pl.ColorIndex = 6;
TestDraw(pl);
let path = new Path(pts);
let path_0 = PathGeneratorSingle.Allocate(path);//获得唯一path
PathGeneratorSingle.RegisterId(path_0);//注册id
return path_0;
});
//选择零件
let plsRes = await app.Editor.GetSelection({
Filter: { filterTypes: [Polyline, Circle, Board] }
});
@ -261,14 +317,6 @@ export async function InitParts(binPath = DefaultBin): Promise<NestDatabase>
pls.push(e as Polyline);
}
//清理缓存
PathGeneratorSingle.Clear();
NestCache.Clear();
DefaultBin.InsideNFPCache = [];
binPath.Id = undefined;
PathGeneratorSingle.RegisterId(binPath);
//曲线转零件
let parts = Curves2Parts(pls, binPath, 3.5) as Part<Curve[], Matrix4>[];
//板件转零件
@ -295,6 +343,7 @@ export async function InitParts(binPath = DefaultBin): Promise<NestDatabase>
db.Paths = PathGeneratorSingle.paths;
db.Parts = parts;
db.Bin = DefaultBin;
db.OddmentsBins = oddpaths;
for (let part of parts)
{

@ -29,7 +29,25 @@ export class Command_TestNFP implements Command
return;
let pls = plsRes.SelectSet.SelectEntityList as (Polyline | Circle)[];
if (pls.length < 2)
{
if (pls.length === 1)
{
plsRes = await app.Editor.GetSelection({
Msg: "选择动的。",
Filter: {
filterTypes: [Polyline, Circle]
}
});
if (plsRes.Status === PromptStatus.OK)
{
pls.push(...plsRes.SelectSet.SelectEntityList as (Polyline | Circle)[]);
if (pls.length < 2) return;
}
}
else
return;
};
Log("黄色代表外部,红色代表内部。");
let binpl = pls.shift();

@ -0,0 +1,73 @@
import { TestDraw } from "../../Add-on/test/TestUtil";
import { app } from "../../ApplicationServices/Application";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { Command } from "../../Editor/CommandMachine";
import { PromptStatus } from "../../Editor/PromptResult";
import { AsVector3 } from "../../Geometry/GeUtils";
import { clipperCpp, InitClipperCpp } from "../Common/ClipperCpp";
import { Path2Polyline } from "../Converter/Path2Polyline";
import { NestCache } from "../Core/NestCache";
import { Path, PathScale } from "../Core/Path";
let sqPath = NestCache.CreatePath(60, 60, 0);
let paths = [
NestCache.CreatePath(200, 200, 0),
NestCache.CreatePath(600, 100, 0),
NestCache.CreatePath(100, 600, 0)
];
export class Command_TestSimplyOddments implements Command
{
async exec()
{
let promise = InitClipperCpp();
let plsRes = await app.Editor.GetSelection({
Msg: "选择多段线",
Filter: {
filterTypes: [Polyline]
}
});
if (plsRes.Status !== PromptStatus.OK) return;
await promise;
let pls = plsRes.SelectSet.SelectEntityList as Polyline[];
for (let pl of pls)
{
let path = new Path(pl.GetStretchPoints(), 0);
let insideNFPS = path.GetInsideNFP(sqPath);
if (insideNFPS)
for (let nfp of insideNFPS)
{
let pts = PathScale(nfp, 1e-4);
let pl2 = Path2Polyline(pts);
pl2.Position = pl.BoundingBox.min;
// TestDraw(pl2, 1);
let nfpPath = new Path(pts);
//还原正确的轮廓
let sum = clipperCpp.lib.minkowskiSumPath(nfpPath.BigIntPoints, sqPath.BigIntPoints, true);
sum = clipperCpp.lib.simplifyPolygons(sum);
for (let poly of sum)
{
if (clipperCpp.lib.area(poly) < 0) continue;
let pts = PathScale(poly, 1e-4);
let tempPath = new Path(pts);
if (paths.some(p => tempPath.GetInsideNFP(p)?.length))
{
let pl = Path2Polyline(pts);
pl.Position = pl2.Position.add(AsVector3(nfpPath.OrigionMinPoint));
TestDraw(pl, 6);
}
}
}
}
}
}

@ -0,0 +1,73 @@
import { TestDraw } from "../../Add-on/test/TestUtil";
import { app } from "../../ApplicationServices/Application";
import { Circle } from "../../DatabaseServices/Entity/Circle";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { Command } from "../../Editor/CommandMachine";
import { PromptStatus } from "../../Editor/PromptResult";
import { AsVector3 } from "../../Geometry/GeUtils";
import { clipperCpp, InitClipperCpp } from "../Common/ClipperCpp";
import { Curves2Points } from "../Converter/ConverBoard2Part";
import { Path2Polyline } from "../Converter/Path2Polyline";
import { NestCache } from "../Core/NestCache";
import { Path, PathScale } from "../Core/Path";
export class Command_TestSum implements Command
{
async exec()
{
let w = InitClipperCpp();
let plsRes = await app.Editor.GetSelection({
Msg: "先选择不动的,在选择动的。",
Filter: {
filterTypes: [Polyline, Circle]
}
});
await w;
if (plsRes.Status !== PromptStatus.OK)
return;
let pls = plsRes.SelectSet.SelectEntityList as (Polyline | Circle)[];
if (pls.length < 2)
{
if (pls.length === 1)
{
plsRes = await app.Editor.GetSelection({
Msg: "选择动的。",
Filter: {
filterTypes: [Polyline, Circle]
}
});
if (plsRes.Status === PromptStatus.OK)
{
pls.push(...plsRes.SelectSet.SelectEntityList as (Polyline | Circle)[]);
if (pls.length < 2) return;
}
}
else
return;
};
let binpl = pls.shift();
let binPath = new Path(Curves2Points(binpl, false, 0)[1]);
let paths: Path[] = pls.map(pl => { return new Path(Curves2Points(pl, true, 0)[1]); });
for (let i = 0; i < paths.length; i++)
{
let sum = clipperCpp.lib.minkowskiSumPath(binPath.BigIntPoints, paths[i].BigIntPoints, true);
sum = clipperCpp.lib.simplifyPolygons(sum);
for (let poly of sum)
{
if (clipperCpp.lib.area(poly) < 0) continue;
let pts = PathScale(poly, 1e-4);
let pl = Path2Polyline(pts);
pl.ColorIndex = 6;
pl.Position = AsVector3(binPath.OrigionMinPoint);
TestDraw(pl);
}
}
}
}

@ -13,6 +13,7 @@ export class Command_TestYH2 implements Command
let m = new OptimizeMachine;
m.Bin = db.Bin;
m.OddmentsBins = db.OddmentsBins;
m.PutParts(db.Parts);
m.callBack = async (inv) =>
{

Loading…
Cancel
Save