!1168 功能:矩形变酒格,命令Rect2Winerack

pull/1168/MERGE
ZoeLeeFZ 4 years ago committed by ChenX
parent 50ff76a9d9
commit e16bd26a07

@ -3,12 +3,13 @@ import { arrayLast, arrayRemoveDuplicateBySort, arraySortByNumber } from "../../
import { Board } from "../../DatabaseServices/Entity/Board";
import { Line } from "../../DatabaseServices/Entity/Line";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { equaln, MoveMatrix, polar, XAxis, YAxis, ZAxis, AsVector2 } from "../../Geometry/GeUtils";
import { equaln, MoveMatrix, polar, XAxis, YAxis, ZAxis, AsVector2, isParallelTo, equalv3 } from "../../Geometry/GeUtils";
import { ISpaceParse } from "../../Geometry/SpaceParse/ISpaceParse";
import { IntersectOption } from "../../GraphicsSystem/IntersectWith";
import { EFullDir, EFullType, EWRackArrayType, IWineRackOption } from "../../UI/Store/WineRackInterface";
import { EFullDir, EFullType, EWRackArrayType, IWineRackOption, IR2WROption } from "../../UI/Store/WineRackInterface";
import { DrawWineRackTool, SIN45 } from "./DrawWinRackTool";
import { GetMirrorMat } from "../../Common/Matrix4Utils";
import { BoardType } from "../../UI/Store/BoardInterface";
export interface IWineRackData
{
@ -24,6 +25,9 @@ export interface IParsePlRes
length?: number;
matInv?: Matrix4;
basePt?: Vector3;
thickness?: number;
isVer?: boolean;
isRo?: boolean;
}
@ -508,7 +512,7 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
return pl;
}
/**根据多段线组构建酒格 */
private DrawBoardFormPolyLine(pls: Polyline[], actualWidth?: number)
protected DrawBoardFormPolyLine(pls: Polyline[], actualWidth?: number, config?: IR2WROption)
{
const leftData: IParsePlRes[] = [];
const rightData: IParsePlRes[] = [];
@ -523,10 +527,9 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
rightData.push(data);
}
this.CreateBoard(leftData, rightData, actualWidth);
this.CreateBoard(rightData, leftData, actualWidth);
this.CreateBoard(leftData, rightData, actualWidth, config);
this.CreateBoard(rightData, leftData, actualWidth, config);
}
/**分析酒格正面多段线 */
private ParsePolyLine(pl: Polyline): IParsePlRes
{
if (pl.IsClose)
@ -540,26 +543,39 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
let pts = pl.GetStretchPoints();
if (equalv3(pts[0], arrayLast(pts)))
pts.pop();
/*****
* **** h
* * *
* 0 **** t
*/
pts.sort((a, b) => a.y - b.y);
pts.sort((a, b) =>
{
if (equaln(a.y, b.y))
return a.x - b.x;
else
return a.y - b.y;
});
let pts1 = pts.splice(0, 2);
pts1.sort((a, b) => a.x - b.x);
pts.sort((a, b) => a.distanceTo(pts1[1]) - b.distanceTo(pts1[1]));
pts.unshift(...pts1);
const area = pl.Area;
let vecX = pts[1].clone().clone().sub(pts[0]);
let vecY = arrayLast(pts).clone().sub(pts[0]);
let v1 = pts[1].clone().clone().sub(pts[0]);
let v2 = arrayLast(pts).clone().sub(pts[0]);
let [vecY, vecX] = v1.length() > v2.length() ? [v1, v2] : [v2, v1];
let length = vecY.length();
let thick = vecX.length();
let mat = new Matrix4().makeBasis(vecX.normalize(), vecY.normalize(), ZAxis).setPosition(pts[0]);
let matInv = new Matrix4().getInverse(mat);
let bp = pts[0].clone().negate();
if (equaln(length * thick, area, 0.01))
{
let an = vecY.angleTo(XAxis);
@ -574,6 +590,8 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
length,
matInv,
basePt: new Vector3(pts[0].x, 0, pts[0].y),
thickness: thick,
isRo: true,
};
}
else if (equaln(an, 3 * Math.PI / 4))
@ -586,6 +604,35 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
length,
matInv,
basePt: new Vector3(pts[0].x, 0, pts[0].y),
thickness: thick,
isRo: true,
};
}
else if (isParallelTo(vecX, XAxis))
{
return {
isOk: true,
isLeft: false,
pl,
length,
matInv: new Matrix4().setPosition(bp),
basePt: new Vector3(pts[0].x, 0, pts[0].y),
thickness: thick,
isVer: true,
isRo: false,
};
}
else if (isParallelTo(vecX, YAxis))
{
return {
isOk: true,
isLeft: true,
pl,
length,
matInv: new Matrix4().setPosition(bp),
basePt: new Vector3(pts[1].x, 0, pts[1].y),
thickness: thick,
isRo: false,
};
}
else
@ -602,12 +649,30 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
};
}
/**绘制酒格 */
private CreateBoard(lData: IParsePlRes[], rData: IParsePlRes[], actualWidth?: number)
private CreateBoard(lData: IParsePlRes[], rData: IParsePlRes[], actualWidth?: number, cof?: IR2WROption)
{
if (!lData.length)
return;
const config = this.Config;
let leftSeal: number, rightSeal: number, upSeal: number, downSeal: number, knifeRad: number;
let config: IWineRackOption;
if (cof)
{
leftSeal = cof.sealedLeft;
rightSeal = cof.sealedRight;
upSeal = cof.sealedUp;
downSeal = cof.sealedDown;
knifeRad = cof.knifeRadius;
}
else
{
config = this.Config;
leftSeal = config.leftEdge;
rightSeal = config.leftEdge;
upSeal = config.leftEdge;
downSeal = config.leftEdge;
knifeRad = config.grooveLengthAdd;
}
for (let i = 0; i < lData.length; i++)
{
@ -618,29 +683,39 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
let intPts = d.pl.IntersectWith(d2.pl, IntersectOption.OnBothOperands);
if (intPts.length === 4)
{
dists.push(...intPts.map(p => Math.abs(p.applyMatrix4(d.matInv).y)));
dists.push(...intPts.map(p =>
{
let p1 = p.applyMatrix4(d.matInv);
if (!d.isRo && !d.isVer)
return Math.abs(p1.x);
return Math.abs(p1.y);
}));
}
}
arraySortByNumber(dists);
arrayRemoveDuplicateBySort(dists, equaln);
let pl = this.GetPolyline(d.length, dists, d.isLeft);
let pl = this.GetPolyline(d, dists, cof);
if (pl)
{
let br = Board.CreateBoard(1, 1, config.boardThick);
let br = Board.CreateBoard(1, 1, d.thickness ?? config.boardThick, d.isVer ? BoardType.Vertical : BoardType.Layer);
if (d.isLeft)
br.Name = "右板" + (lData.length - i);
else
br.Name = "左板" + (i + 1);
br.ContourCurve = pl;
this.ParseHighSealing(br, config.leftEdge, config.rightEdge, config.topEdge, config.bottomEdge, d.isLeft);
this.ParseHighSealing(br, leftSeal, rightSeal, upSeal, downSeal, d.isLeft);
br.KnifeRadius = knifeRad;
if (d.isRo)
br.RotateBoard(0, Math.PI / 4 * (d.isLeft ? -1 : 1), 0);
br.ApplyMatrix(MoveMatrix(this.space.SpaceBox.min));
br.ApplyMatrix(MoveMatrix(d.basePt));
if ((config.fullType === EFullType.ByHeight || config.arrayType === EWRackArrayType.Fixed)
if (config && (config.fullType === EFullType.ByHeight || config.arrayType === EWRackArrayType.Fixed)
&& config.fullDir === EFullDir.Right)
{
br.ApplyMatrix(MoveMatrix(new Vector3(this.space.Size.x - actualWidth)));
br.ApplyMatrix(MoveMatrix(new Vector3(this.space.Size.x - (actualWidth ?? 0))));
}
br.ApplyMatrix(this.space.SpaceOCS);
this.boardlist.push(br);
@ -648,22 +723,43 @@ export class DrawObliqueWineRackTool extends DrawWineRackTool
}
}
/**构建酒格形状,加入齿 */
private GetPolyline(len: number, dists: number[], isLeft: boolean)
private GetPolyline(data: IParsePlRes, dists: number[], cof?: IR2WROption)
{
let len = data.length;
let isLeft = data.isLeft;
const size = this.space.Size;
let pl = new Polyline();
pl.Rectangle(size.y, len);
let addWidth: number;
let leftEdge: number;
let rightEdge: number;
let knifeRad: number;
if (cof)
{
addWidth = cof.addLen;
leftEdge = cof.sealedLeft;
rightEdge = cof.sealedRight;
knifeRad = cof.knifeRadius;
}
else
{
const config = this.Config;
let addWidth = config.grooveWidthAdd;
let leftEdge = config.leftEdge;
let rightEdge = config.rightEdge;
let knifeRad = config.grooveLengthAdd;
addWidth = config.grooveWidthAdd;
leftEdge = config.leftEdge;
rightEdge = config.rightEdge;
knifeRad = config.grooveLengthAdd;
}
if (isLeft)
{
if (!data.isVer)
{
let newDist = dists.map(d => len - d);
dists.length = 0;
dists.push(...newDist);
}
addWidth = (addWidth - 2 * leftEdge) / 2;
}
else

@ -0,0 +1,168 @@
import { Command } from "../../Editor/CommandMachine";
import { app } from "../../ApplicationServices/Application";
import { PromptStatus } from "../../Editor/PromptResult";
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { ISpaceParse } from "../../Geometry/SpaceParse/ISpaceParse";
import { Box3Ext } from "../../Geometry/Box";
import { Vector3, Matrix4, Box3 } from "three";
import { Rect2Winerack } from "../../UI/Components/Board/Rect2Winerack";
import { rect2WinerackStore } from "../../UI/Store/Rect2WinerackStore";
import { ModalState } from "../../UI/Components/Modal/ModalInterface";
import { toJS } from "mobx";
import { Rect2WinerackTool } from "./Rect2WinerackTool";
import { matrixSetVector } from "../../Common/Matrix4Utils";
import { MergeCurvelist } from "../../Common/CurveUtils";
import { ProcessingGroupRecord } from "../../DatabaseServices/ProcessingGroup/ProcessingGroupRecord";
export class Polyline2Winerack implements Command
{
async exec()
{
let plRes = await app.Editor.GetSelection({
Msg: "选择酒格封闭矩形",
Filter: { filterTypes: [Polyline] }
});
if (plRes.Status === PromptStatus.OK)
{
app.Editor.ModalManage.RenderModal(Rect2Winerack, { store: rect2WinerackStore });
let res = await app.Editor.ModalManage.Wait();
if (res.Status === ModalState.Cancel) return;
let pllist = plRes.SelectSet.SelectEntityList as Polyline[];
let plss = this.Classify(pllist.filter(c => c.IsClose));
let tool = Rect2WinerackTool.GetInstance() as Rect2WinerackTool;
let ucsInv = new Matrix4().getInverse(app.Editor.UCSMatrix);
for (let pls of plss)
{
const box = new Box3Ext();
let wcsBox = new Box3Ext();
for (let pl of pls)
{
box.union(pl.BoundingBox);
pl.ApplyMatrix(ucsInv);
wcsBox.union(pl.BoundingBox);
}
let min = box.min;
let max = box.max;
let minWcs = wcsBox.min;
const option = toJS(rect2WinerackStore.option);
let mtx = app.Editor.UCSMatrix;
mtx.setPosition(min);
let mtxInv = new Matrix4().getInverse(mtx);
let p1 = min.clone().applyMatrix4(mtxInv);
let p2 = max.clone().applyMatrix4(mtxInv).setZ(0);
let spaceBox = new Box3Ext().setFromPoints([p1, p2]);
spaceBox.max.setZ(option.depth);
//设置坐标系原点
p1.copy(spaceBox.min).applyMatrix4(mtx);
mtx.setPosition(p1);
spaceBox.max.sub(spaceBox.min);
spaceBox.min.set(0, 0, 0);
let y = new Vector3().setFromMatrixColumn(mtx, 1);
let z = new Vector3().setFromMatrixColumn(mtx, 2);
matrixSetVector(mtx, 1, z.negate());
matrixSetVector(mtx, 2, y);
[spaceBox.min.y, spaceBox.min.z] = [spaceBox.min.z, spaceBox.min.y];
[spaceBox.max.y, spaceBox.max.z] = [spaceBox.max.z, spaceBox.max.y];
mtxInv.getInverse(mtx);
let space = new ISpaceParse([], mtx);
space.SpaceBox = spaceBox;
for (let pl of pls)
{
pl.Position = pl.Position.sub(minWcs);
pl.Erase();
}
tool.ParseFromRects(pls, space, toJS(rect2WinerackStore.option));
tool.boardlist.forEach(b => app.Database.ModelSpace.Append(b));
//添加加工组
let ng = new ProcessingGroupRecord();
ng.Name = "酒格";
app.Database.ProcessingGroupTable.Add(ng);
for (let en of tool.boardlist)
{
ng.Objects.push(en.Id);
en.ProcessingGroupList.push(ng.Id);
}
}
}
}
private Classify(pllist: Polyline[])
{
let plss: Polyline[][] = [];
let boxCache = new WeakMap<Polyline, Box3>();
//合并共线线段
for (let i = 0; i < pllist.length; i++)
{
let pl = pllist[i];
if (pl.EndParam !== 4)
{
let cus = pl.Explode();
MergeCurvelist(cus);
pllist[i] = Polyline.Combine(cus);
}
}
while (pllist.length > 0)
{
let firstPl = pllist.shift();
let arr = [firstPl];
let box = firstPl.BoundingBox;
while (true)
{
let remPls = pllist.filter(l =>
{
let bo = boxCache.get(l);
if (!bo)
{
bo = l.BoundingBox;
boxCache.set(l, bo);
}
if (bo.intersectsBox(box))
{
box.union(bo);
arr.push(l);
return false;
}
return true;
});
if (remPls.length === pllist.length)
{
plss.push(arr);
break;
}
else
pllist = remPls;
}
}
return plss;
}
}

@ -0,0 +1,14 @@
import { Polyline } from "../../DatabaseServices/Entity/Polyline";
import { ISpaceParse } from "../../Geometry/SpaceParse/ISpaceParse";
import { IR2WROption } from "../../UI/Store/WineRackInterface";
import { DrawObliqueWineRackTool } from "./DrawObliqueWineRackTool";
export class Rect2WinerackTool extends DrawObliqueWineRackTool
{
ParseFromRects(pls: Polyline[], space: ISpaceParse, config: IR2WROption)
{
this.space = space;
this.boardlist.length = 0;
this.DrawBoardFormPolyLine(pls, undefined, config);
}
}

@ -168,4 +168,5 @@ export enum CommandNames
SelectAll = "SELECTALL",
CheckHoles = "CHECKHOLES",
CombinatAttributeBrush = "COMBINATATTRIBUTEBRUSH",//复合实体刷
Rect2Winerack = "RECT2WINERACK",//复合实体刷
}

@ -321,6 +321,9 @@ export class ExtrudeSolid extends Entity
{
if (!curve.IsClose) return;
let area = curve.Area;
if (!area || equaln(area, 0)) return;
if (curve instanceof Spline || curve instanceof Ellipse)
curve = curve.Convert2Polyline();

@ -187,6 +187,7 @@ import { Command_EntitytMoveToZ0 } from "../Add-on/EntityMoveToZ0";
import { ChangeColorByMaterial } from "../Add-on/ChangeColorByBoardMaterial";
import { CheckHoles } from "../Add-on/CheckHoles";
import { CombinatAttributeBrush } from "../Add-on/CombinatAttributeBrush";
import { Polyline2Winerack } from "../Add-on/DrawWineRack/Polyline2Winerack";
export function registerCommand()
{
@ -523,6 +524,8 @@ export function registerCommand()
commandMachine.RegisterCommand(CommandNames.CheckHoles, new CheckHoles());
commandMachine.RegisterCommand(CommandNames.CombinatAttributeBrush, new CombinatAttributeBrush());
commandMachine.RegisterCommand(CommandNames.Rect2Winerack, new Polyline2Winerack());
}
export async function RegistCustomCommand()

@ -73,6 +73,12 @@ interface P2
export function equalv3(v1: Vector3, v2: Vector3, fuzz = 1e-8)
{
if (!v1 || !v2)
{
console.error("传入点数据错误");
return false;
}
return equaln(v1.x, v2.x, fuzz) && equaln(v1.y, v2.y, fuzz) && equaln(v1.z, v2.z, fuzz);
}
export function equalv2(v1: P2, v2: P2, fuzz = 1e-8)

@ -46,6 +46,7 @@ export enum BoardModalType
SmoothEdge = "smoothEdge",
UpdateBoardInfo = "updateboardinfo",
YX = "yixing",
R2WR = "R2WR",
}
export interface BoardModalProps
{

@ -0,0 +1,140 @@
import * as React from 'react';
import { CommonModal } from '../Modal/ModalContainer';
import { app } from '../../../ApplicationServices/Application';
import { Label, H5, Button, Intent, Card } from '@blueprintjs/core';
import { Rect2WinerackStore } from '../../Store/Rect2WinerackStore';
import { ToasterInput } from '../Toaster';
import { CheckObjectType } from '../../../Common/CheckoutVaildValue';
import { EBoardKeyList } from '../../../Common/BoardKeyList';
import { BoardModalType } from './BoardModal';
import { end } from 'xaop';
import { KeyBoard } from '../../../Common/KeyEnum';
import { observer } from 'mobx-react';
interface IRect2WinerackProps
{
store: Rect2WinerackStore;
}
@observer
export class Rect2Winerack extends React.Component<IRect2WinerackProps> {
private event: Function;
componentDidMount()
{
this.event = end(app.Editor.ModalManage, app.Editor.ModalManage.OnKeyDown, (e: KeyboardEvent) =>
{
if (Rect2Winerack.name !== app.Editor.ModalManage.CurrentModalKey) return;
if (e.keyCode === KeyBoard.Enter || e.keyCode === KeyBoard.Space)
{
this.ok();
e.preventDefault();
}
e.stopPropagation();
return true;
});
}
componentWillUnmount()
{
this.event();
this.event = null;
}
public render()
{
const store = this.props.store;
return (
<CommonModal
title="绘制酒格"
close={this.cancel}
type={BoardModalType.R2WR}
store={store}
footerChildren={
<>
<Button
text="确定"
intent={Intent.SUCCESS}
onClick={this.ok}
/>
<Button
text="取消"
onClick={this.cancel}
/>
</>
}
>
<Card className="flex-center" onKeyDown={this.handleKeydown}>
<div style={{ width: 150 }}>
<Label>
<span style={{ width: 70 }}>:</span>
<ToasterInput
type={CheckObjectType.GT0Num}
option={store.option}
uiOption={store.UIOption}
optKey="depth"
/>
</Label>
<Label>
<span style={{ width: 70 }}>齿</span>
<ToasterInput
type={CheckObjectType.WR}
option={store.option}
uiOption={store.UIOption}
optKey="addLen"
/>
</Label>
<Label>
<span style={{ width: 70 }}></span>
<ToasterInput
type={CheckObjectType.BR}
option={store.option}
uiOption={store.UIOption}
optKey="knifeRadius"
/>
</Label>
</div>
<div className="center">
<H5></H5>
<ToasterInput
type={CheckObjectType.BR}
option={store.option}
uiOption={store.UIOption}
optKey={EBoardKeyList.UpSealed}
/>
<div className="flex">
<ToasterInput
type={CheckObjectType.BR}
option={store.option}
uiOption={store.UIOption}
optKey={EBoardKeyList.LeftSealed}
/>
<ToasterInput
type={CheckObjectType.BR}
option={store.option}
uiOption={store.UIOption}
optKey={EBoardKeyList.RightSealed}
/>
</div>
<ToasterInput
type={CheckObjectType.BR}
option={store.option}
uiOption={store.UIOption}
optKey={EBoardKeyList.DownSealed}
/>
</div>
</Card>
</CommonModal>
);
}
private handleKeydown = (e: React.KeyboardEvent<HTMLDivElement>) =>
{
console.log(e.keyCode);
};
private ok = () =>
{
app.Editor.ModalManage.DestoryAndExec();
};
private cancel = () =>
{
app.Editor.ModalManage.Destory();
};
}

@ -384,6 +384,16 @@ export const CommandList: ICommand[] = [
chName: "矩形变板件",
chDes: "将矩形转换为板件",
},
{
icon: IconEnum.Rect2Winerack,
typeId: "hb",
link: "#",
defaultCustom: "R2WR",
command: CommandNames.Rect2Winerack,
type: "画板",
chName: "矩形变酒格",
chDes: "将矩形转换为酒格",
},
//板件编辑
{

@ -75,6 +75,7 @@ export class TopToolBar extends React.Component<{}, {}>
{ svg: IconEnum.ChangeColorByMat, title: "根据板材修改颜色", command: CommandNames.ChangeColorByMaterial },
{ svg: IconEnum.RestoreColor, title: "恢复板件颜色", command: CommandNames.RestoreColor },
{ svg: IconEnum.Cabrush, title: "复合实体属性刷", command: CommandNames.CombinatAttributeBrush },
{ svg: IconEnum.Rect2Winerack, title: "矩形变酒格", command: CommandNames.Rect2Winerack },
];
store.iconList.brEdit = [
{ svg: IconEnum.QG, title: "线性切割", command: CommandNames.LinearCutting },

@ -169,4 +169,5 @@ export enum IconEnum
Cabrush = "Cabrush.svg",
Convert2Polyline = "Convert2Polyline.svg",
CheckHole = "CheckHoles.svg",
Rect2Winerack = "Rect2Winerack.svg",
}

@ -0,0 +1,62 @@
import { IConfigStore } from "./BoardStore";
import { IR2WROption } from "./WineRackInterface";
import { observable, toJS } from "mobx";
import { EBoardKeyList } from "../../Common/BoardKeyList";
import { IUiOption } from "./BoardInterface";
import { DataAdapter } from "../../Common/DataAdapter";
import { IConfigOption } from "../Components/Board/UserConfig";
export class Rect2WinerackStore implements IConfigStore
{
@observable configName = "默认";
@observable configsNames = [];
@observable option: IR2WROption = {
depth: 350,
addLen: 0,
knifeRadius: 3,
[EBoardKeyList.UpSealed]: 1,
[EBoardKeyList.DownSealed]: 1,
[EBoardKeyList.LeftSealed]: 1,
[EBoardKeyList.RightSealed]: 1,
};
uiOption: IUiOption<IR2WROption>;
get UIOption()
{
if (!this.uiOption)
this.uiOption = DataAdapter.ConvertUIData(this.option);
return this.uiOption;
}
InitOption()
{
Object.assign(this.option, {
depth: 350,
addLen: 0,
knifeRadius: 3,
[EBoardKeyList.UpSealed]: 1,
[EBoardKeyList.DownSealed]: 1,
[EBoardKeyList.LeftSealed]: 1,
[EBoardKeyList.RightSealed]: 1,
});
if (!this.uiOption)
this.uiOption = DataAdapter.ConvertUIData(this.option);
else
{
Object.assign(this.uiOption, DataAdapter.ConvertUIData(this.option));
}
}
SaveConfig()
{
return {
option: toJS(this.option)
};
}
UpdateOption(cof: IConfigOption<IR2WROption>)
{
Object.assign(this.option, cof.option);
if (this.uiOption)
Object.assign(this.uiOption, DataAdapter.ConvertUIData(cof.option));
}
}
export const rect2WinerackStore = new Rect2WinerackStore();

@ -1,4 +1,5 @@
import { IBaseOption } from "./BoardInterface";
import { EBoardKeyList } from "../../Common/BoardKeyList";
export enum EWineRackType
{
@ -57,3 +58,14 @@ export interface IWineRackOption extends IBaseOption
isDrawVer: boolean;
brThick2: number; //补板厚
}
export interface IR2WROption
{
depth: number;
addLen: number;
knifeRadius: number;
[EBoardKeyList.UpSealed]: number;
[EBoardKeyList.DownSealed]: number;
[EBoardKeyList.LeftSealed]: number;
[EBoardKeyList.RightSealed]: number;
}

Loading…
Cancel
Save