mirror of https://gitee.com/cf-fz/WebCAD.git
parent
932b36437f
commit
bbda2d0a14
@ -0,0 +1,154 @@
|
|||||||
|
import { Vector2 } from "./Vector2";
|
||||||
|
import { Point } from "./Point";
|
||||||
|
|
||||||
|
export class Box2
|
||||||
|
{
|
||||||
|
min: Vector2;
|
||||||
|
max: Vector2;
|
||||||
|
constructor(min = new Vector2(+ Infinity, + Infinity), max = new Vector2(- Infinity, - Infinity))
|
||||||
|
{
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
set(min: Vector2, max: Vector2): Box2
|
||||||
|
{
|
||||||
|
this.min.copy(min);
|
||||||
|
this.max.copy(max);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setFromPoints(points: Vector2[]): Box2
|
||||||
|
{
|
||||||
|
this.makeEmpty();
|
||||||
|
for (let i = 0, il = points.length; i < il; i++)
|
||||||
|
{
|
||||||
|
this.expandByPoint(points[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private static _setFromCenterAndSize_v1 = new Vector2();
|
||||||
|
setFromCenterAndSize(center: Vector2, size: Vector2): Box2
|
||||||
|
{
|
||||||
|
const v1 = Box2._setFromCenterAndSize_v1;
|
||||||
|
const halfSize = v1.copy(size).multiplyScalar(0.5);
|
||||||
|
this.min.copy(center).sub(halfSize);
|
||||||
|
this.max.copy(center).add(halfSize);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
clone(): Box2
|
||||||
|
{
|
||||||
|
return new (this.constructor as any)().copy(this);
|
||||||
|
}
|
||||||
|
copy(box: Box2): Box2
|
||||||
|
{
|
||||||
|
this.min.copy(box.min);
|
||||||
|
this.max.copy(box.max);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
makeEmpty(): Box2
|
||||||
|
{
|
||||||
|
this.min.x = this.min.y = + Infinity;
|
||||||
|
this.max.x = this.max.y = - Infinity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isEmpty(): boolean
|
||||||
|
{
|
||||||
|
// this is a more robust check for empty than (volume <= 0) because volume can get positive with two negative axes
|
||||||
|
return (this.max.x < this.min.x) || (this.max.y < this.min.y);
|
||||||
|
}
|
||||||
|
getCenter(result: Vector2 = new Vector2()): Vector2
|
||||||
|
{
|
||||||
|
return this.isEmpty() ? result.set(0, 0) : result.addVectors(this.min, this.max).multiplyScalar(0.5);
|
||||||
|
}
|
||||||
|
getSize(result: Vector2 = new Vector2()): Vector2
|
||||||
|
{
|
||||||
|
return this.isEmpty() ? result.set(0, 0) : result.subVectors(this.max, this.min);
|
||||||
|
}
|
||||||
|
expandByPoint(point: Vector2): Box2
|
||||||
|
{
|
||||||
|
this.min.min(point);
|
||||||
|
this.max.max(point);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
expandByVector(vector: Vector2): Box2
|
||||||
|
{
|
||||||
|
this.min.sub(vector);
|
||||||
|
this.max.add(vector);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
expandByScalar(scalar: number): Box2
|
||||||
|
{
|
||||||
|
this.min.addScalar(- scalar);
|
||||||
|
this.max.addScalar(scalar);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
containsPoint(point: Vector2): boolean
|
||||||
|
{
|
||||||
|
if (point.x < this.min.x || point.x > this.max.x ||
|
||||||
|
point.y < this.min.y || point.y > this.max.y)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
containsBox(box: Box2): boolean
|
||||||
|
{
|
||||||
|
if ((this.min.x <= box.min.x) && (box.max.x <= this.max.x) &&
|
||||||
|
(this.min.y <= box.min.y) && (box.max.y <= this.max.y))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
getParameter(point: Vector2, result: Vector2 = new Vector2()): Vector2
|
||||||
|
{
|
||||||
|
// This can potentially have a divide by zero if the box
|
||||||
|
// has a size dimension of 0.
|
||||||
|
return result.set(
|
||||||
|
(point.x - this.min.x) / (this.max.x - this.min.x),
|
||||||
|
(point.y - this.min.y) / (this.max.y - this.min.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
intersectsBox(box: Box2): boolean
|
||||||
|
{
|
||||||
|
// using 6 splitting planes to rule out intersections.
|
||||||
|
if (box.max.x < this.min.x || box.min.x > this.max.x ||
|
||||||
|
box.max.y < this.min.y || box.min.y > this.max.y)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
clampPoint(point: Vector2, result: Vector2 = new Vector2()): Vector2
|
||||||
|
{
|
||||||
|
return result.copy(point).clamp(this.min, this.max);
|
||||||
|
}
|
||||||
|
private static _distanceToPoint_v1 = new Vector2();
|
||||||
|
distanceToPoint(point: Vector2): number
|
||||||
|
{
|
||||||
|
const v1 = Box2._distanceToPoint_v1;
|
||||||
|
const clampedPoint = v1.copy(point).clamp(this.min, this.max);
|
||||||
|
return clampedPoint.sub(point).length();
|
||||||
|
}
|
||||||
|
intersect(box: Box2): Box2
|
||||||
|
{
|
||||||
|
this.min.max(box.min);
|
||||||
|
this.max.min(box.max);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
union(box: Box2): Box2
|
||||||
|
{
|
||||||
|
this.min.min(box.min);
|
||||||
|
this.max.max(box.max);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
translate(offset: Point): Box2
|
||||||
|
{
|
||||||
|
this.min.add(offset);
|
||||||
|
this.max.add(offset);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
equals(box: Box2): boolean
|
||||||
|
{
|
||||||
|
return box.min.equals(this.min) && box.max.equals(this.max);
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,21 @@
|
|||||||
|
import * as clipperLib from "js-angusj-clipper/web"; // nodejs style require
|
||||||
|
|
||||||
|
export let clipperCpp: { lib?: clipperLib.ClipperLibWrapper; } = {};
|
||||||
|
export function InitClipperCpp(): Promise<void>
|
||||||
|
{
|
||||||
|
if (clipperCpp.lib) return;
|
||||||
|
if (!globalThis.document)
|
||||||
|
globalThis.document = {} as any;
|
||||||
|
return new Promise((res, rej) =>
|
||||||
|
{
|
||||||
|
clipperLib.loadNativeClipperLibInstanceAsync(
|
||||||
|
// let it autodetect which one to use, but also available WasmOnly and AsmJsOnly
|
||||||
|
clipperLib.NativeClipperLibRequestedFormat.WasmWithAsmJsFallback
|
||||||
|
).then(c =>
|
||||||
|
{
|
||||||
|
clipperCpp.lib = c;
|
||||||
|
res();
|
||||||
|
console.log("载入成功!");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
import { Path } from "./Path";
|
||||||
|
let width = 1221;
|
||||||
|
let height = 2441;
|
||||||
|
export let DefaultBin = new Path([{ x: 0, y: 0 }, { x: width, y: 0 }, { x: width, y: height }, { x: 0, y: height }]);
|
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* CAD文件数据
|
||||||
|
*/
|
||||||
|
export class CADFiler
|
||||||
|
{
|
||||||
|
private readIndex: number = 0;
|
||||||
|
constructor(public _datas: any[] = []) { }
|
||||||
|
|
||||||
|
Clear()
|
||||||
|
{
|
||||||
|
this._datas.length = 0;
|
||||||
|
return this.Reset();
|
||||||
|
}
|
||||||
|
Reset()
|
||||||
|
{
|
||||||
|
this.readIndex = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(data: any)
|
||||||
|
{
|
||||||
|
this._datas.push(data);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Read(): any
|
||||||
|
{
|
||||||
|
return this._datas[this.readIndex++];
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,27 @@
|
|||||||
const ctx: Worker = self as any;
|
const ctx: Worker = self as any;
|
||||||
ctx.addEventListener("message", (event) =>
|
import { InitClipperCpp } from "./ClipperCpp";
|
||||||
|
import { CADFiler } from "./Filer";
|
||||||
|
import { NestDatabase } from "./NestDatabase";
|
||||||
|
import { OptimizeMachine } from "./OptimizeMachine";
|
||||||
|
|
||||||
|
ctx.addEventListener("message", async (event) =>
|
||||||
{
|
{
|
||||||
|
await InitClipperCpp();
|
||||||
|
let f = new CADFiler(event.data);
|
||||||
|
let db = new NestDatabase;
|
||||||
|
db.ReadFile(f);
|
||||||
|
|
||||||
|
let m = new OptimizeMachine;
|
||||||
|
m.Bin = db.Bin;
|
||||||
|
m.PutParts(db.Parts);
|
||||||
|
|
||||||
|
m.callBack = async (inv) =>
|
||||||
|
{
|
||||||
|
let f = new CADFiler();
|
||||||
|
inv.WriteFile(f);
|
||||||
|
ctx.postMessage(f._datas);
|
||||||
|
};
|
||||||
|
await m.Start();
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {} as typeof Worker & (new () => Worker);
|
export default {} as typeof Worker & (new () => Worker);
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
import { Clipper, ClipType, Paths, PolyFillType, PolyType } from "clipper-js-fpoint";
|
|
||||||
import { Box3, Vector3 } from "three";
|
|
||||||
import { TestDraw } from "../../Add-on/test/TestUtil";
|
|
||||||
import { app } from "../../ApplicationServices/Application";
|
|
||||||
import { arrayRemoveIf } from "../../Common/ArrayExt";
|
|
||||||
import { JigUtils } from "../../Editor/JigUtils";
|
|
||||||
import { AsVector3, equaln } from "../../Geometry/GeUtils";
|
|
||||||
import { Container } from "../Container";
|
|
||||||
import { Path2Polyline } from "../Converter/Path2Polyline";
|
|
||||||
import { NestCache } from "../NestCache";
|
|
||||||
import { Part } from "../Part";
|
|
||||||
|
|
||||||
export class TestContainer extends Container
|
|
||||||
{
|
|
||||||
async PutPart2(part: Part): Promise<boolean>
|
|
||||||
{
|
|
||||||
//------------先放置在孔洞内------------
|
|
||||||
if (this.Holes)
|
|
||||||
{
|
|
||||||
for (let c of this.Holes)
|
|
||||||
{
|
|
||||||
if (c.PutPart(part))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cacheKey = this.StatusKey + "," + part.State.Contour.Id;
|
|
||||||
//读取缓存位置
|
|
||||||
if (NestCache.PositionCache.has(cacheKey))
|
|
||||||
{
|
|
||||||
NestCache.count1++;
|
|
||||||
let p = NestCache.PositionCache.get(cacheKey);
|
|
||||||
part.PlacePosition = p;
|
|
||||||
this.AppendPartHoles(part);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------无法容纳------------
|
|
||||||
if (this.BinPath.Area - this.PlacedArea < part.State.Contour.Area)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
let binNfp = this.BinPath.GetInsideNFP(part.State.Contour);
|
|
||||||
if (!binNfp || binNfp.length === 0) return false;
|
|
||||||
|
|
||||||
//------------首个------------
|
|
||||||
if (this.Placed.length === 0)
|
|
||||||
{
|
|
||||||
part.PlacePosition = this.GetFarLeftP(binNfp);
|
|
||||||
this.AppendPartHoles(part);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//如果已经缓存过,证明已经无法放置(否则应该已经缓存了放置点)
|
|
||||||
// if (NestCache.PartCaled.has(cacheKey)) return false;
|
|
||||||
// NestCache.PartCaled.add(cacheKey);
|
|
||||||
|
|
||||||
//------------计算nfp------------
|
|
||||||
//合并(零件和所有已经放置零件的NFP)
|
|
||||||
let clipper = new Clipper();
|
|
||||||
|
|
||||||
let i = 1;
|
|
||||||
for (let placedPart of this.Placed)
|
|
||||||
{
|
|
||||||
let nfp = placedPart.State.Contour.GetOutsideNFP(part.State.Contour);
|
|
||||||
if (!nfp)
|
|
||||||
return false;
|
|
||||||
let x = placedPart.PlacePosition.x;
|
|
||||||
let y = placedPart.PlacePosition.y;
|
|
||||||
|
|
||||||
if (i === 2 && this.Placed.length === 4)
|
|
||||||
{
|
|
||||||
let pl = placedPart.State.Contour.Polyline.Clone();
|
|
||||||
let pl2 = part.State.Contour.Polyline.Clone();
|
|
||||||
|
|
||||||
pl.Position = pl.Position.add(new Vector3(0, -5000));
|
|
||||||
pl2.Position = pl2.Position.add(new Vector3(0, -5000));
|
|
||||||
|
|
||||||
TestDraw(pl, 1);
|
|
||||||
TestDraw(pl2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let n of nfp)
|
|
||||||
{
|
|
||||||
let f = n.map(v => { return { x: v.x + x, y: v.y + y }; });
|
|
||||||
|
|
||||||
//绘制所有的NFP
|
|
||||||
let pl = Path2Polyline(f);
|
|
||||||
pl.ColorIndex = i;
|
|
||||||
JigUtils.Draw(pl);
|
|
||||||
|
|
||||||
clipper.AddPath(f, PolyType.ptSubject, true);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Editor.UpdateScreen();
|
|
||||||
await app.Editor.GetPoint();
|
|
||||||
|
|
||||||
let combinedNfp = [];
|
|
||||||
if (!clipper.Execute(ClipType.ctUnion, combinedNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (combinedNfp.length === 0) return false;
|
|
||||||
|
|
||||||
//binNfp 减去 combinedNfp 得到最终的nfp
|
|
||||||
clipper = new Clipper();
|
|
||||||
clipper.AddPaths(binNfp, PolyType.ptSubject, true);
|
|
||||||
clipper.AddPaths(combinedNfp, PolyType.ptClip, true);
|
|
||||||
let finalNfp: Paths = [];
|
|
||||||
if (!clipper.Execute(ClipType.ctDifference, finalNfp, PolyFillType.pftNonZero, PolyFillType.pftNonZero))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//清理NFP
|
|
||||||
finalNfp = Clipper.CleanPolygons(finalNfp, 1);
|
|
||||||
arrayRemoveIf(finalNfp, nfp =>
|
|
||||||
{
|
|
||||||
return (nfp.length < 3 || Math.abs(Clipper.Area(nfp)) < 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (finalNfp.length === 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//绘制结果nfp
|
|
||||||
JigUtils.End();
|
|
||||||
for (let n of finalNfp)
|
|
||||||
{
|
|
||||||
let pl = Path2Polyline(n);
|
|
||||||
pl.ColorIndex = 6;
|
|
||||||
JigUtils.Draw(pl);
|
|
||||||
}
|
|
||||||
app.Editor.UpdateScreen();
|
|
||||||
await app.Editor.GetPoint();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 选择零件放置后的最优占地空间(box).
|
|
||||||
* 使用凸包看起来不是最佳的
|
|
||||||
* TODO:使用重力方向
|
|
||||||
*/
|
|
||||||
let minWidth = Infinity;
|
|
||||||
let minArea: number = Infinity;
|
|
||||||
let minX: number = Infinity;
|
|
||||||
let minY: number = Infinity;
|
|
||||||
let minBox: Box3;
|
|
||||||
let translate: Vector3;
|
|
||||||
for (let nfp of finalNfp)
|
|
||||||
{
|
|
||||||
for (let p of nfp)
|
|
||||||
{
|
|
||||||
let translateTemp = AsVector3(p);
|
|
||||||
let box2 = part.State.Contour.BoundingBox.clone();
|
|
||||||
box2.translate(translateTemp);
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
minArea = area;
|
|
||||||
minWidth = size.x;
|
|
||||||
translate = translateTemp;
|
|
||||||
minX = translateTemp.x;
|
|
||||||
minY = translateTemp.y;
|
|
||||||
minBox = rectBox;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (translate)
|
|
||||||
{
|
|
||||||
part.PlacePosition = translate;
|
|
||||||
this.AppendPartHoles(part);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
import { Command } from "../../Editor/CommandMachine";
|
|
||||||
import { app } from "../../ApplicationServices/Application";
|
|
||||||
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
|
||||||
import { PromptStatus } from "../../Editor/PromptResult";
|
|
||||||
import { Polylin2Points } from "../Converter/ConverBoard2Part";
|
|
||||||
import { TestDraw } from "../../Add-on/test/TestUtil";
|
|
||||||
import { Path2Polyline } from "../Converter/Path2Polyline";
|
|
||||||
import { HotCMD } from "../../Hot/HotCommand";
|
|
||||||
|
|
||||||
@HotCMD
|
|
||||||
export class Command_Polyline2Path implements Command
|
|
||||||
{
|
|
||||||
async exec()
|
|
||||||
{
|
|
||||||
let plRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Polyline] } });
|
|
||||||
if (plRes.Status !== PromptStatus.OK) return;
|
|
||||||
let pl = plRes.Entity as Polyline;
|
|
||||||
|
|
||||||
|
|
||||||
// TestDraw(Path2Polyline(Polylin2Points(pl, true, 3)), 1);
|
|
||||||
// TestDraw(Path2Polyline(Polylin2Points(pl, false, 3)), 2);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
import { Matrix4 } from "three";
|
|
||||||
import { TestDraw } from "../../Add-on/test/TestUtil";
|
|
||||||
import { app } from "../../ApplicationServices/Application";
|
|
||||||
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
|
||||||
import { PromptStatus } from "../../Editor/PromptResult";
|
|
||||||
import { HotCMD } from "../../Hot/HotCommand";
|
|
||||||
import { DefaultBin } from "../Converter/ConverBoard2Part";
|
|
||||||
import { Curve2Path } from "../Converter/Curves2Parts";
|
|
||||||
import { Part } from "../Part";
|
|
||||||
import { PathGeneratorSingle } from "../PathGenerator";
|
|
||||||
|
|
||||||
@HotCMD
|
|
||||||
export class Test_Translate
|
|
||||||
{
|
|
||||||
async exec()
|
|
||||||
{
|
|
||||||
PathGeneratorSingle.Clear();
|
|
||||||
|
|
||||||
let eRes = await app.Editor.GetEntity({ Filter: { filterTypes: [Polyline] } });
|
|
||||||
if (eRes.Status !== PromptStatus.OK) return;
|
|
||||||
let pl = eRes.Entity as Polyline;
|
|
||||||
|
|
||||||
let path = Curve2Path(pl, true);
|
|
||||||
|
|
||||||
let part = new Part();
|
|
||||||
part.Init(path, DefaultBin, 36);
|
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < part.RotatedStates.length; i++)
|
|
||||||
{
|
|
||||||
let p = part.RotatedStates[i];
|
|
||||||
let plx = pl.Clone();
|
|
||||||
let m = new Matrix4().setPosition(-p.OrigionMinPoint.x, -p.OrigionMinPoint.y, 0);
|
|
||||||
plx.ApplyMatrix(m);
|
|
||||||
|
|
||||||
let r = new Matrix4().makeRotationZ(p.Rotation);
|
|
||||||
plx.ApplyMatrix(r);
|
|
||||||
TestDraw(plx, i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,179 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Matrix4, Vector3 } from "three";
|
|
||||||
import { TestDraw } from "../../Add-on/test/TestUtil";
|
|
||||||
import { app } from "../../ApplicationServices/Application";
|
|
||||||
import { Board } from "../../DatabaseServices/Entity/Board";
|
|
||||||
import { Circle } from "../../DatabaseServices/Entity/Circle";
|
|
||||||
import { Curve } from "../../DatabaseServices/Entity/Curve";
|
|
||||||
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
|
||||||
import { Command, commandMachine } from "../../Editor/CommandMachine";
|
|
||||||
import { JigUtils } from "../../Editor/JigUtils";
|
|
||||||
import { PromptStatus } from "../../Editor/PromptResult";
|
|
||||||
import { AsVector3, IdentityMtx4 } from "../../Geometry/GeUtils";
|
|
||||||
import { HotCMD } from "../../Hot/HotCommand";
|
|
||||||
import { AppToaster } from "../../UI/Components/Toaster";
|
|
||||||
import { ConverBoard2Part, DefaultBin } from "../Converter/ConverBoard2Part";
|
|
||||||
import { Curves2Parts } from "../Converter/Curves2Parts";
|
|
||||||
import { NestCache } from "../NestCache";
|
|
||||||
import { OptimizeMachine } from "../OptimizeMachine";
|
|
||||||
import { TestContainer } from "./TestContainer";
|
|
||||||
import { NestVariant } from "./TestVariant";
|
|
||||||
|
|
||||||
@HotCMD
|
|
||||||
export class Command_TestYH implements Command
|
|
||||||
{
|
|
||||||
async exec()
|
|
||||||
{
|
|
||||||
NestVariant.curveMap.clear();
|
|
||||||
let plsRes = await app.Editor.GetSelection({
|
|
||||||
Filter: {
|
|
||||||
filterTypes: [Polyline, Circle, Board]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (plsRes.Status !== PromptStatus.OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let pls: (Polyline | Circle)[] = [];
|
|
||||||
let brs: Board[] = [];
|
|
||||||
for (let e of plsRes.SelectSet.SelectEntityList)
|
|
||||||
{
|
|
||||||
if (e instanceof Board)
|
|
||||||
brs.push(e);
|
|
||||||
else
|
|
||||||
pls.push(e as Polyline);
|
|
||||||
}
|
|
||||||
|
|
||||||
let binPath = DefaultBin;
|
|
||||||
|
|
||||||
let parts = Curves2Parts(pls, binPath, 3);
|
|
||||||
for (let br of brs)
|
|
||||||
{
|
|
||||||
parts.push(ConverBoard2Part(br));
|
|
||||||
}
|
|
||||||
|
|
||||||
//购买机器
|
|
||||||
let machie = new OptimizeMachine();
|
|
||||||
machie.Bin = binPath;
|
|
||||||
machie.PutParts(parts);
|
|
||||||
|
|
||||||
let key = AppToaster.show({
|
|
||||||
message: <>
|
|
||||||
持续优化中....
|
|
||||||
<button onClick={e =>
|
|
||||||
{
|
|
||||||
machie.Suspend();
|
|
||||||
AppToaster.dismiss(key);
|
|
||||||
}}>停止</button>
|
|
||||||
</>,
|
|
||||||
timeout: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
let binCurves: Polyline[] = [];
|
|
||||||
const DrawBin = (count: number) =>
|
|
||||||
{
|
|
||||||
for (let i = binCurves.length; i < count; i++)
|
|
||||||
{
|
|
||||||
let pl = binPath.Polyline.Clone();
|
|
||||||
pl.Position = new Vector3(1.1 * i * 1220);
|
|
||||||
TestDraw(pl, 6);
|
|
||||||
binCurves.push(pl);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < binCurves.length; i++)
|
|
||||||
{
|
|
||||||
binCurves[i].Visible = i < count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
for (let part of parts)
|
|
||||||
{
|
|
||||||
for (let c of part.UserData)
|
|
||||||
NestVariant.curveMap.set(c, c.OCS);
|
|
||||||
}
|
|
||||||
machie.callBack = async (i) =>
|
|
||||||
{
|
|
||||||
let size = binPath.BoundingBox.getSize(new Vector3).setY(0);
|
|
||||||
size.x *= 1.1;
|
|
||||||
let p = AsVector3(binPath.OrigionMinPoint);
|
|
||||||
DrawBin(i.Containers.length);
|
|
||||||
for (let c of i.Containers)
|
|
||||||
{
|
|
||||||
let ocs = new Matrix4().setPosition(p);
|
|
||||||
let i = 1;
|
|
||||||
for (let p of c.PlacedAll)
|
|
||||||
{
|
|
||||||
let curves = p.UserData as Curve[];
|
|
||||||
for (let c of curves)
|
|
||||||
{
|
|
||||||
c.ColorIndex = i;
|
|
||||||
c.OCS = NestVariant.curveMap.get(c) || IdentityMtx4;
|
|
||||||
c.ApplyMatrix(p.PlaceCS)
|
|
||||||
.ApplyMatrix(p.Container.OCS)
|
|
||||||
.ApplyMatrix(ocs);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
p.add(size);
|
|
||||||
}
|
|
||||||
app.Editor.UpdateScreen();
|
|
||||||
};
|
|
||||||
await machie.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@HotCMD
|
|
||||||
class RePlace
|
|
||||||
{
|
|
||||||
async exec()
|
|
||||||
{
|
|
||||||
if (!NestVariant.best) return;
|
|
||||||
NestCache.Clear();
|
|
||||||
|
|
||||||
let binPath = DefaultBin;
|
|
||||||
let size = binPath.BoundingBox.getSize(new Vector3).setY(0);
|
|
||||||
size.x *= 1.1;
|
|
||||||
let p = new Vector3;
|
|
||||||
// for (let c of NestVariant.best.Containers)
|
|
||||||
let c = NestVariant.best.Containers[1];
|
|
||||||
{
|
|
||||||
let ocs = new Matrix4().setPosition(p);
|
|
||||||
|
|
||||||
let nc = new TestContainer(binPath);
|
|
||||||
let parts = c.PlacedAll.map(p => p.Clone());
|
|
||||||
|
|
||||||
let i = 1;
|
|
||||||
for (let p of parts)
|
|
||||||
{
|
|
||||||
JigUtils.End();
|
|
||||||
await nc.PutPart2(p);
|
|
||||||
let curves = p.UserData as Curve[];
|
|
||||||
if (curves[0].IsErase)
|
|
||||||
{
|
|
||||||
curves = curves.map(c =>
|
|
||||||
{
|
|
||||||
let nc = c.Clone();
|
|
||||||
TestDraw(nc);
|
|
||||||
return nc;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let c of curves)
|
|
||||||
{
|
|
||||||
c.ColorIndex = i;
|
|
||||||
c.OCS = NestVariant.curveMap.get(c) || IdentityMtx4;
|
|
||||||
c.ApplyMatrix(p.PlaceCS)
|
|
||||||
.ApplyMatrix(p.Container.OCS)
|
|
||||||
.ApplyMatrix(ocs);
|
|
||||||
}
|
|
||||||
JigUtils.End();
|
|
||||||
await app.Editor.GetPoint({ Msg: "放置完毕" });
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
p.add(size);
|
|
||||||
}
|
|
||||||
app.Editor.UpdateScreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commandMachine.RegisterCommand("reyh", new RePlace);
|
|
@ -1,57 +0,0 @@
|
|||||||
import { Command } from "../../Editor/CommandMachine";
|
|
||||||
import { app } from "../../ApplicationServices/Application";
|
|
||||||
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
|
||||||
import { Circle } from "../../DatabaseServices/Entity/Circle";
|
|
||||||
import { Board } from "../../DatabaseServices/Entity/Board";
|
|
||||||
import { PromptStatus } from "../../Editor/PromptResult";
|
|
||||||
import { DefaultBin, ConverBoard2Part } from "../Converter/ConverBoard2Part";
|
|
||||||
import { Curves2Parts } from "../Converter/Curves2Parts";
|
|
||||||
import { PathGeneratorSingle } from "../PathGenerator";
|
|
||||||
import { NestDatabase } from "../NestDatabase";
|
|
||||||
import { CADFiler } from "../../DatabaseServices/CADFiler";
|
|
||||||
import { HotCMD } from "../../Hot/HotCommand";
|
|
||||||
|
|
||||||
@HotCMD
|
|
||||||
export class Command_TestYH2 implements Command
|
|
||||||
{
|
|
||||||
async exec()
|
|
||||||
{
|
|
||||||
PathGeneratorSingle.Clear();
|
|
||||||
|
|
||||||
PathGeneratorSingle.RegisterId(DefaultBin);
|
|
||||||
|
|
||||||
let plsRes = await app.Editor.GetSelection({
|
|
||||||
Filter: {
|
|
||||||
filterTypes: [Polyline, Circle, Board]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (plsRes.Status !== PromptStatus.OK)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let pls: (Polyline | Circle)[] = [];
|
|
||||||
let brs: Board[] = [];
|
|
||||||
for (let e of plsRes.SelectSet.SelectEntityList)
|
|
||||||
{
|
|
||||||
if (e instanceof Board)
|
|
||||||
brs.push(e);
|
|
||||||
else
|
|
||||||
pls.push(e as Polyline);
|
|
||||||
}
|
|
||||||
|
|
||||||
let binPath = DefaultBin;
|
|
||||||
|
|
||||||
let parts = Curves2Parts(pls, binPath, 3);
|
|
||||||
for (let br of brs)
|
|
||||||
parts.push(ConverBoard2Part(br));
|
|
||||||
|
|
||||||
|
|
||||||
let db = new NestDatabase();
|
|
||||||
db.Paths = PathGeneratorSingle.paths;
|
|
||||||
db.Parts = parts;
|
|
||||||
db.Bin = DefaultBin;
|
|
||||||
|
|
||||||
let f = new CADFiler();
|
|
||||||
db.WriteFile(f);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,182 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Matrix4, Vector3 } from "three";
|
||||||
|
import { TestDraw } from "../../Add-on/test/TestUtil";
|
||||||
|
import { app } from "../../ApplicationServices/Application";
|
||||||
|
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||||
|
import { Circle } from "../../DatabaseServices/Entity/Circle";
|
||||||
|
import { Curve } from "../../DatabaseServices/Entity/Curve";
|
||||||
|
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
||||||
|
import { Command } from "../../Editor/CommandMachine";
|
||||||
|
import { PromptStatus } from "../../Editor/PromptResult";
|
||||||
|
import { AsVector3, IdentityMtx4 } from "../../Geometry/GeUtils";
|
||||||
|
import { HotCMD } from "../../Hot/HotCommand";
|
||||||
|
import { AppToaster } from "../../UI/Components/Toaster";
|
||||||
|
import { ConverBoard2Part } from "../Converter/ConverBoard2Part";
|
||||||
|
import { Curves2Parts } from "../Converter/Curves2Parts";
|
||||||
|
import { Path2Polyline } from "../Converter/Path2Polyline";
|
||||||
|
import { DefaultBin } from "../DefaultBin";
|
||||||
|
import { NestCache } from "../NestCache";
|
||||||
|
import { NestDatabase } from "../NestDatabase";
|
||||||
|
import { OptimizeMachine } from "../OptimizeMachine";
|
||||||
|
import { PathGeneratorSingle } from "../PathGenerator";
|
||||||
|
import { NestVariant } from "./TestVariant";
|
||||||
|
|
||||||
|
@HotCMD
|
||||||
|
export class Command_TestYH2 implements Command
|
||||||
|
{
|
||||||
|
async exec()
|
||||||
|
{
|
||||||
|
PathGeneratorSingle.Clear();
|
||||||
|
NestCache.Clear();
|
||||||
|
DefaultBin.InsideNFPCache = {};
|
||||||
|
|
||||||
|
let plsRes = await app.Editor.GetSelection({
|
||||||
|
Filter: {
|
||||||
|
filterTypes: [Polyline, Circle, Board]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (plsRes.Status !== PromptStatus.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let pls: (Polyline | Circle)[] = [];
|
||||||
|
let brs: Board[] = [];
|
||||||
|
for (let e of plsRes.SelectSet.SelectEntityList)
|
||||||
|
{
|
||||||
|
if (e instanceof Board)
|
||||||
|
brs.push(e);
|
||||||
|
else
|
||||||
|
pls.push(e as Polyline);
|
||||||
|
}
|
||||||
|
|
||||||
|
let binPath = DefaultBin;
|
||||||
|
binPath.Id = undefined;
|
||||||
|
PathGeneratorSingle.RegisterId(binPath);
|
||||||
|
|
||||||
|
let parts = Curves2Parts(pls, binPath, 3);
|
||||||
|
for (let br of brs)
|
||||||
|
parts.push(ConverBoard2Part(br));
|
||||||
|
|
||||||
|
parts = parts.filter(p => p.RotatedStates.length > 0);
|
||||||
|
if (parts.length === 0)
|
||||||
|
{
|
||||||
|
app.Editor.Prompt("没有可以优化的板件!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < parts.length; i++)
|
||||||
|
parts[i].Id = i;
|
||||||
|
|
||||||
|
let db = new NestDatabase();
|
||||||
|
db.Paths = PathGeneratorSingle.paths;
|
||||||
|
db.Parts = parts;
|
||||||
|
db.Bin = DefaultBin;
|
||||||
|
|
||||||
|
let m = new OptimizeMachine;
|
||||||
|
m.Bin = db.Bin;
|
||||||
|
m.PutParts(db.Parts);
|
||||||
|
|
||||||
|
let binCurves: Polyline[] = [];
|
||||||
|
const DrawBin = (count: number) =>
|
||||||
|
{
|
||||||
|
for (let i = binCurves.length; i < count; i++)
|
||||||
|
{
|
||||||
|
let pl = Path2Polyline(binPath.Points);
|
||||||
|
pl.Position = new Vector3(1.1 * i * 1220);
|
||||||
|
TestDraw(pl, 6);
|
||||||
|
binCurves.push(pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < binCurves.length; i++)
|
||||||
|
{
|
||||||
|
binCurves[i].Visible = i < count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let part of parts)
|
||||||
|
{
|
||||||
|
for (let c of part.UserData)
|
||||||
|
NestVariant.curveMap.set(c, c.OCS);
|
||||||
|
}
|
||||||
|
|
||||||
|
m.callBack = async (inv) =>
|
||||||
|
{
|
||||||
|
for (let part of parts)
|
||||||
|
{
|
||||||
|
part.Parent = undefined;
|
||||||
|
for (let c of part.UserData)
|
||||||
|
{
|
||||||
|
c.OCS = NestVariant.curveMap.get(c) || IdentityMtx4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawBin(inv.Containers.length);
|
||||||
|
for (let i = 0; i < inv.Containers.length; i++)
|
||||||
|
{
|
||||||
|
for (let part of inv.Containers[i].Placed)
|
||||||
|
{
|
||||||
|
let r = new Matrix4().makeRotationZ(part.State.Rotation);
|
||||||
|
let m = new Matrix4().setPosition(-part.State.OrigionMinPoint.x - 0.5, -part.State.OrigionMinPoint.y - 0.5, 0);
|
||||||
|
let p = new Matrix4().setPosition(AsVector3(part.PlacePosition).multiplyScalar(1e-4));
|
||||||
|
|
||||||
|
let partOld = parts[part.Id];
|
||||||
|
partOld.PlaceCS = p.multiply(r).multiply(m);
|
||||||
|
if (i !== 0)
|
||||||
|
partOld.PlaceCS = new Matrix4().setPosition(new Vector3(1.1 * i * 1220)).multiply(partOld.PlaceCS);
|
||||||
|
|
||||||
|
for (let c of partOld.UserData)
|
||||||
|
{
|
||||||
|
let cu = c as Curve;
|
||||||
|
cu.ApplyMatrix(partOld.PlaceCS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hole of inv.HoleContainers)
|
||||||
|
{
|
||||||
|
let pm = new Matrix4().setPosition(hole.ParentM.x, hole.ParentM.y, 0);
|
||||||
|
for (let part of hole.Placed)
|
||||||
|
{
|
||||||
|
let r = new Matrix4().makeRotationZ(part.State.Rotation);
|
||||||
|
let m = new Matrix4().setPosition(-part.State.OrigionMinPoint.x, -part.State.OrigionMinPoint.y, 0);
|
||||||
|
let p = new Matrix4().setPosition(AsVector3(part.PlacePosition).multiplyScalar(1e-4));
|
||||||
|
part.PlaceCS = pm.clone().multiply(p).multiply(r).multiply(m);//pm.multiply (p).multiply pm.multiply(p).multiply
|
||||||
|
part.Parent = parts[hole.ParentId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hole of inv.HoleContainers)
|
||||||
|
{
|
||||||
|
for (let part of hole.Placed)
|
||||||
|
{
|
||||||
|
for (let c of part.UserData)
|
||||||
|
{
|
||||||
|
let cu = c as Curve;
|
||||||
|
cu.Visible = true;
|
||||||
|
cu.ApplyMatrix(part.PlaceCS);
|
||||||
|
let p = part.Parent;
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
cu.ApplyMatrix(p.PlaceCS);
|
||||||
|
p = p.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = AppToaster.show({
|
||||||
|
message: <>
|
||||||
|
持续优化中....
|
||||||
|
<button onClick={e =>
|
||||||
|
{
|
||||||
|
m.Suspend();
|
||||||
|
AppToaster.dismiss(key);
|
||||||
|
}}>停止</button>
|
||||||
|
</>,
|
||||||
|
timeout: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
await m.Start();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,212 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Matrix4, Vector3 } from "three";
|
||||||
|
import { TestDraw } from "../../Add-on/test/TestUtil";
|
||||||
|
import { app } from "../../ApplicationServices/Application";
|
||||||
|
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||||
|
import { Circle } from "../../DatabaseServices/Entity/Circle";
|
||||||
|
import { Curve } from "../../DatabaseServices/Entity/Curve";
|
||||||
|
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
|
||||||
|
import { Command, CommandWrap } from "../../Editor/CommandMachine";
|
||||||
|
import { PromptStatus } from "../../Editor/PromptResult";
|
||||||
|
import { AsVector3, IdentityMtx4 } from "../../Geometry/GeUtils";
|
||||||
|
import { HotCMD } from "../../Hot/HotCommand";
|
||||||
|
import { AppToaster } from "../../UI/Components/Toaster";
|
||||||
|
import { InitClipperCpp } from "../ClipperCpp";
|
||||||
|
import { ConverBoard2Part } from "../Converter/ConverBoard2Part";
|
||||||
|
import { Curves2Parts } from "../Converter/Curves2Parts";
|
||||||
|
import { Path2Polyline } from "../Converter/Path2Polyline";
|
||||||
|
import { DefaultBin } from "../DefaultBin";
|
||||||
|
import { CADFiler } from "../Filer";
|
||||||
|
import { Individual } from "../Individual";
|
||||||
|
import { NestCache } from "../NestCache";
|
||||||
|
import { NestDatabase } from "../NestDatabase";
|
||||||
|
import Worker from "../OptimizeWorker.worker";
|
||||||
|
import { PathGeneratorSingle } from "../PathGenerator";
|
||||||
|
import { NestVariant } from "./TestVariant";
|
||||||
|
|
||||||
|
@HotCMD
|
||||||
|
export class Command_TestYHWorker implements Command
|
||||||
|
{
|
||||||
|
async exec()
|
||||||
|
{
|
||||||
|
PathGeneratorSingle.Clear();
|
||||||
|
NestCache.Clear();
|
||||||
|
DefaultBin.InsideNFPCache = {};
|
||||||
|
|
||||||
|
let plsRes = await app.Editor.GetSelection({
|
||||||
|
Filter: { filterTypes: [Polyline, Circle, Board] }
|
||||||
|
});
|
||||||
|
|
||||||
|
await InitClipperCpp();
|
||||||
|
if (plsRes.Status !== PromptStatus.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let pls: (Polyline | Circle)[] = [];
|
||||||
|
let brs: Board[] = [];
|
||||||
|
for (let e of plsRes.SelectSet.SelectEntityList)
|
||||||
|
{
|
||||||
|
if (e instanceof Board)
|
||||||
|
brs.push(e);
|
||||||
|
else
|
||||||
|
pls.push(e as Polyline);
|
||||||
|
}
|
||||||
|
|
||||||
|
let binPath = DefaultBin;
|
||||||
|
binPath.Id = undefined;
|
||||||
|
PathGeneratorSingle.RegisterId(binPath);
|
||||||
|
|
||||||
|
let parts = Curves2Parts(pls, binPath, 3.5);
|
||||||
|
for (let br of brs)
|
||||||
|
parts.push(ConverBoard2Part(br));
|
||||||
|
|
||||||
|
parts = parts.filter(p => p.RotatedStates.length > 0);
|
||||||
|
if (parts.length === 0)
|
||||||
|
{
|
||||||
|
app.Editor.Prompt("没有可以优化的板件!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < parts.length; i++)
|
||||||
|
parts[i].Id = i;
|
||||||
|
|
||||||
|
let db = new NestDatabase();
|
||||||
|
db.Paths = PathGeneratorSingle.paths;
|
||||||
|
db.Parts = parts;
|
||||||
|
db.Bin = DefaultBin;
|
||||||
|
|
||||||
|
let f = new CADFiler();
|
||||||
|
db.WriteFile(f);
|
||||||
|
|
||||||
|
let db2 = new NestDatabase();
|
||||||
|
f.Reset();
|
||||||
|
db2.ReadFile(f);
|
||||||
|
|
||||||
|
let binCurves: Polyline[] = [];
|
||||||
|
const DrawBin = (count: number) =>
|
||||||
|
{
|
||||||
|
for (let i = binCurves.length; i < count; i++)
|
||||||
|
{
|
||||||
|
let pl = Path2Polyline(binPath.Points);
|
||||||
|
pl.Position = new Vector3(1.1 * i * 1220);
|
||||||
|
TestDraw(pl, 6);
|
||||||
|
binCurves.push(pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < binCurves.length; i++)
|
||||||
|
{
|
||||||
|
binCurves[i].Visible = i < count;
|
||||||
|
}
|
||||||
|
app.Editor.UpdateScreen();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
for (let part of parts)
|
||||||
|
{
|
||||||
|
for (let c of part.UserData)
|
||||||
|
NestVariant.curveMap.set(c, c.OCS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let workers: Worker[] = [];
|
||||||
|
let key = AppToaster.show({
|
||||||
|
message: <>
|
||||||
|
持续优化中....
|
||||||
|
<button onClick={e =>
|
||||||
|
{
|
||||||
|
for (let w of workers)
|
||||||
|
w.terminate();
|
||||||
|
AppToaster.dismiss(key);
|
||||||
|
}}>停止</button>
|
||||||
|
</>,
|
||||||
|
timeout: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let best = Infinity;
|
||||||
|
const fff = (e) =>
|
||||||
|
{
|
||||||
|
let f = new CADFiler(e.data);
|
||||||
|
let inv = new Individual(db.Parts);
|
||||||
|
inv.ReadFile(f);
|
||||||
|
if (best <= inv.Fitness)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
best = inv.Fitness;
|
||||||
|
CommandWrap(() =>
|
||||||
|
{
|
||||||
|
app.Editor.Prompt(`优化率:${inv.Fitness}`);
|
||||||
|
|
||||||
|
for (let part of parts)
|
||||||
|
{
|
||||||
|
part.Parent = undefined;
|
||||||
|
for (let c of part.UserData)
|
||||||
|
{
|
||||||
|
c.OCS = NestVariant.curveMap.get(c) || IdentityMtx4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawBin(inv.Containers.length);
|
||||||
|
for (let i = 0; i < inv.Containers.length; i++)
|
||||||
|
{
|
||||||
|
let j = 1;
|
||||||
|
for (let part of inv.Containers[i].Placed)
|
||||||
|
{
|
||||||
|
let r = new Matrix4().makeRotationZ(part.State.Rotation);
|
||||||
|
let m = new Matrix4().setPosition(-part.State.OrigionMinPoint.x - 0.5, -part.State.OrigionMinPoint.y - 0.5, 0);
|
||||||
|
let p = new Matrix4().setPosition(AsVector3(part.PlacePosition).multiplyScalar(1e-4));
|
||||||
|
|
||||||
|
let partOld = parts[part.Id];
|
||||||
|
partOld.PlaceCS = p.multiply(r).multiply(m);
|
||||||
|
if (i !== 0)
|
||||||
|
partOld.PlaceCS = new Matrix4().setPosition(new Vector3(1.1 * i * 1220)).multiply(partOld.PlaceCS);
|
||||||
|
|
||||||
|
for (let c of partOld.UserData)
|
||||||
|
{
|
||||||
|
let cu = c as Curve;
|
||||||
|
cu.ColorIndex = j;
|
||||||
|
cu.ApplyMatrix(partOld.PlaceCS);
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hole of inv.HoleContainers)
|
||||||
|
{
|
||||||
|
let pm = new Matrix4().setPosition(hole.ParentM.x, hole.ParentM.y, 0);
|
||||||
|
for (let part of hole.Placed)
|
||||||
|
{
|
||||||
|
let r = new Matrix4().makeRotationZ(part.State.Rotation);
|
||||||
|
let m = new Matrix4().setPosition(-part.State.OrigionMinPoint.x, -part.State.OrigionMinPoint.y, 0);
|
||||||
|
let p = new Matrix4().setPosition(AsVector3(part.PlacePosition).multiplyScalar(1e-4));
|
||||||
|
part.PlaceCS = pm.clone().multiply(p).multiply(r).multiply(m);//pm.multiply (p).multiply pm.multiply(p).multiply
|
||||||
|
part.Parent = parts[hole.ParentId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let hole of inv.HoleContainers)
|
||||||
|
{
|
||||||
|
for (let part of hole.Placed)
|
||||||
|
{
|
||||||
|
for (let c of part.UserData)
|
||||||
|
{
|
||||||
|
let cu = c as Curve;
|
||||||
|
cu.Visible = true;
|
||||||
|
cu.ApplyMatrix(part.PlaceCS);
|
||||||
|
let p = part.Parent;
|
||||||
|
while (p)
|
||||||
|
{
|
||||||
|
cu.ApplyMatrix(p.PlaceCS);
|
||||||
|
p = p.Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "应用优化");
|
||||||
|
};
|
||||||
|
for (let i = 0; i < navigator.hardwareConcurrency - 2; i++)// ||
|
||||||
|
{
|
||||||
|
let w = new Worker;
|
||||||
|
workers.push(w);
|
||||||
|
w.postMessage(f._datas);
|
||||||
|
w.onmessage = fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
export function equaln(v1: number, v2: number, fuzz = 1e-5)
|
||||||
|
{
|
||||||
|
return Math.abs(v1 - v2) <= fuzz;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FixIndex(index: number, arr: Array<any> | number)
|
||||||
|
{
|
||||||
|
let count = (arr instanceof Array) ? arr.length : arr;
|
||||||
|
if (index < 0)
|
||||||
|
return count + index;
|
||||||
|
else if (index >= count)
|
||||||
|
return index - count;
|
||||||
|
else
|
||||||
|
return index;
|
||||||
|
}
|
@ -0,0 +1,292 @@
|
|||||||
|
import { Point } from "./Point";
|
||||||
|
|
||||||
|
export class Vector2
|
||||||
|
{
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
readonly isVector2: boolean = true;
|
||||||
|
constructor(x: number = 0, y: number = 0)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
get width(): number { return this.x; }
|
||||||
|
set width(value: number) { this.x = value; }
|
||||||
|
get height(): number { return this.y; }
|
||||||
|
set height(value: number) { this.y = value; }
|
||||||
|
set(x: number, y: number): Vector2
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setScalar(scalar: number): Vector2
|
||||||
|
{
|
||||||
|
this.x = scalar;
|
||||||
|
this.y = scalar;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setX(x: number): Vector2
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setY(y: number): Vector2
|
||||||
|
{
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setComponent(index: number, value: number): Vector2
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: this.x = value; break;
|
||||||
|
case 1: this.y = value; break;
|
||||||
|
default: throw new Error('index is out of range: ' + index);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
getComponent(index: number): number
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0: return this.x;
|
||||||
|
case 1: return this.y;
|
||||||
|
default: throw new Error('index is out of range: ' + index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clone(): Vector2
|
||||||
|
{
|
||||||
|
return new (this.constructor as any)().copy(this);
|
||||||
|
}
|
||||||
|
copy(v: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x = v.x;
|
||||||
|
this.y = v.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
add(v: Point): Vector2
|
||||||
|
{
|
||||||
|
this.x += v.x;
|
||||||
|
this.y += v.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addScalar(s: number): Vector2
|
||||||
|
{
|
||||||
|
this.x += s;
|
||||||
|
this.y += s;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addVectors(a: Vector2, b: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x = a.x + b.x;
|
||||||
|
this.y = a.y + b.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
addScaledVector(v: Vector2, s: number): Vector2
|
||||||
|
{
|
||||||
|
this.x += v.x * s;
|
||||||
|
this.y += v.y * s;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
sub(v: Vector2, w?: Vector2): Vector2
|
||||||
|
{
|
||||||
|
if (w !== undefined)
|
||||||
|
{
|
||||||
|
console.warn('THREE.Vector2: .sub() now only accepts one argument. Use .subVectors(a, b) instead.');
|
||||||
|
return this.subVectors(v, w);
|
||||||
|
}
|
||||||
|
this.x -= v.x;
|
||||||
|
this.y -= v.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
subScalar(s: number): Vector2
|
||||||
|
{
|
||||||
|
this.x -= s;
|
||||||
|
this.y -= s;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
subVectors(a: Vector2, b: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x = a.x - b.x;
|
||||||
|
this.y = a.y - b.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
multiply(v: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x *= v.x;
|
||||||
|
this.y *= v.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
multiplyScalar(scalar: number): Vector2
|
||||||
|
{
|
||||||
|
if (isFinite(scalar))
|
||||||
|
{
|
||||||
|
this.x *= scalar;
|
||||||
|
this.y *= scalar;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
divide(v: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x /= v.x;
|
||||||
|
this.y /= v.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
divideScalar(scalar: number): Vector2
|
||||||
|
{
|
||||||
|
return this.multiplyScalar(1 / scalar);
|
||||||
|
}
|
||||||
|
min(v: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x = Math.min(this.x, v.x);
|
||||||
|
this.y = Math.min(this.y, v.y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
max(v: Vector2): Vector2
|
||||||
|
{
|
||||||
|
this.x = Math.max(this.x, v.x);
|
||||||
|
this.y = Math.max(this.y, v.y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
clamp(min: Vector2, max: Vector2): Vector2
|
||||||
|
{
|
||||||
|
// This function assumes min < max, if this assumption isn't true it will not operate correctly
|
||||||
|
this.x = Math.max(min.x, Math.min(max.x, this.x));
|
||||||
|
this.y = Math.max(min.y, Math.min(max.y, this.y));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private static clampScalar_min = new Vector2();
|
||||||
|
private static clampScalar_max = new Vector2();
|
||||||
|
clampScalar(minVal: number, maxVal: number): Vector2
|
||||||
|
{
|
||||||
|
const min: Vector2 = Vector2.clampScalar_min.set(minVal, minVal);
|
||||||
|
const max: Vector2 = Vector2.clampScalar_max.set(maxVal, maxVal);
|
||||||
|
return this.clamp(min, max);
|
||||||
|
}
|
||||||
|
clampLength(min: number, max: number): Vector2
|
||||||
|
{
|
||||||
|
const length: number = this.length();
|
||||||
|
return this.multiplyScalar(Math.max(min, Math.min(max, length)) / length);
|
||||||
|
}
|
||||||
|
floor(): Vector2
|
||||||
|
{
|
||||||
|
this.x = Math.floor(this.x);
|
||||||
|
this.y = Math.floor(this.y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
ceil(): Vector2
|
||||||
|
{
|
||||||
|
this.x = Math.ceil(this.x);
|
||||||
|
this.y = Math.ceil(this.y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
round(): Vector2
|
||||||
|
{
|
||||||
|
this.x = Math.round(this.x);
|
||||||
|
this.y = Math.round(this.y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
roundToZero(): Vector2
|
||||||
|
{
|
||||||
|
this.x = (this.x < 0) ? Math.ceil(this.x) : Math.floor(this.x);
|
||||||
|
this.y = (this.y < 0) ? Math.ceil(this.y) : Math.floor(this.y);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
negate(): Vector2
|
||||||
|
{
|
||||||
|
this.x = - this.x;
|
||||||
|
this.y = - this.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
dot(v: Vector2): number
|
||||||
|
{
|
||||||
|
return this.x * v.x + this.y * v.y;
|
||||||
|
}
|
||||||
|
lengthSq(): number
|
||||||
|
{
|
||||||
|
return this.x * this.x + this.y * this.y;
|
||||||
|
}
|
||||||
|
length(): number
|
||||||
|
{
|
||||||
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||||
|
}
|
||||||
|
lengthManhattan(): number
|
||||||
|
{
|
||||||
|
return Math.abs(this.x) + Math.abs(this.y);
|
||||||
|
}
|
||||||
|
normalize(): Vector2
|
||||||
|
{
|
||||||
|
return this.divideScalar(this.length());
|
||||||
|
}
|
||||||
|
angle(): number
|
||||||
|
{
|
||||||
|
// computes the angle in radians with respect to the positive x-axis
|
||||||
|
let angle: number = Math.atan2(this.y, this.x);
|
||||||
|
if (angle < 0) angle += 2 * Math.PI;
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
distanceTo(v: Vector2): number
|
||||||
|
{
|
||||||
|
return Math.sqrt(this.distanceToSquared(v));
|
||||||
|
}
|
||||||
|
distanceToSquared(v: Vector2): number
|
||||||
|
{
|
||||||
|
const dx: number = this.x - v.x, dy: number = this.y - v.y;
|
||||||
|
return dx * dx + dy * dy;
|
||||||
|
}
|
||||||
|
distanceToManhattan(v: Vector2): number
|
||||||
|
{
|
||||||
|
return Math.abs(this.x - v.x) + Math.abs(this.y - v.y);
|
||||||
|
}
|
||||||
|
setLength(length: number): Vector2
|
||||||
|
{
|
||||||
|
return this.multiplyScalar(length / this.length());
|
||||||
|
}
|
||||||
|
lerp(v: Vector2, alpha: number): Vector2
|
||||||
|
{
|
||||||
|
this.x += (v.x - this.x) * alpha;
|
||||||
|
this.y += (v.y - this.y) * alpha;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
lerpVectors(v1: Vector2, v2: Vector2, alpha: number): Vector2
|
||||||
|
{
|
||||||
|
return this.subVectors(v2, v1).multiplyScalar(alpha).add(v1);
|
||||||
|
}
|
||||||
|
equals(v: Vector2): boolean
|
||||||
|
{
|
||||||
|
return ((v.x === this.x) && (v.y === this.y));
|
||||||
|
}
|
||||||
|
fromArray(array: Float32Array | number[], offset: number = 0): Vector2
|
||||||
|
{
|
||||||
|
this.x = array[offset];
|
||||||
|
this.y = array[offset + 1];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
toArray(array: Float32Array | number[] = [], offset: number = 0): Float32Array | number[]
|
||||||
|
{
|
||||||
|
array[offset] = this.x;
|
||||||
|
array[offset + 1] = this.y;
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
fromAttribute(attribute: any, index: number, offset: number = 0): Vector2
|
||||||
|
{
|
||||||
|
index = index * attribute.itemSize + offset;
|
||||||
|
this.x = attribute.array[index];
|
||||||
|
this.y = attribute.array[index + 1];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
rotateAround(center: Vector2, angle: number): Vector2
|
||||||
|
{
|
||||||
|
const c: number = Math.cos(angle), s: number = Math.sin(angle);
|
||||||
|
const x: number = this.x - center.x;
|
||||||
|
const y: number = this.y - center.y;
|
||||||
|
this.x = x * c - y * s + center.x;
|
||||||
|
this.y = x * s + y * c + center.y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue